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  *   http://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.*;
30 
31 /**
32  * <strong>Internal usage only!</strong>
33  * <p>Static members which call JNI methods must be defined in {@link ErrorsStaticallyReferencedJniMethods}.
34  */
35 public final class Errors {
36     // As all our JNI methods return -errno on error we need to compare with the negative errno codes.
37     public static final int ERRNO_ENOENT_NEGATIVE = -errnoENOENT();
38     public static final int ERRNO_ENOTCONN_NEGATIVE = -errnoENOTCONN();
39     public static final int ERRNO_EBADF_NEGATIVE = -errnoEBADF();
40     public static final int ERRNO_EPIPE_NEGATIVE = -errnoEPIPE();
41     public static final int ERRNO_ECONNRESET_NEGATIVE = -errnoECONNRESET();
42     public static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN();
43     public static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK();
44     public static final int ERRNO_EINPROGRESS_NEGATIVE = -errnoEINPROGRESS();
45     public static final int ERROR_ECONNREFUSED_NEGATIVE = -errorECONNREFUSED();
46     public static final int ERROR_EISCONN_NEGATIVE = -errorEISCONN();
47     public static final int ERROR_EALREADY_NEGATIVE = -errorEALREADY();
48     public static final int ERROR_ENETUNREACH_NEGATIVE = -errorENETUNREACH();
49 
50     /**
51      * Holds the mappings for errno codes to String messages.
52      * This eliminates the need to call back into JNI to get the right String message on an exception
53      * and thus is faster.
54      *
55      * The array length of 512 should be more then enough because errno.h only holds < 200 codes.
56      */
57     private static final String[] ERRORS = new String[512];
58 
59     /**
60      * <strong>Internal usage only!</strong>
61      */
62     public static final class NativeIoException extends IOException {
63         private static final long serialVersionUID = 8222160204268655526L;
64         private final int expectedErr;
65         private final boolean fillInStackTrace;
66 
NativeIoException(String method, int expectedErr)67         public NativeIoException(String method, int expectedErr) {
68             this(method, expectedErr, true);
69         }
70 
NativeIoException(String method, int expectedErr, boolean fillInStackTrace)71         public NativeIoException(String method, int expectedErr, boolean fillInStackTrace) {
72             super(method + "(..) failed: " + ERRORS[-expectedErr]);
73             this.expectedErr = expectedErr;
74             this.fillInStackTrace = fillInStackTrace;
75         }
76 
expectedErr()77         public int expectedErr() {
78             return expectedErr;
79         }
80 
81         @Override
fillInStackTrace()82         public synchronized Throwable fillInStackTrace() {
83             if (fillInStackTrace) {
84                 return super.fillInStackTrace();
85             }
86             return this;
87         }
88     }
89 
90     static final class NativeConnectException extends ConnectException {
91         private static final long serialVersionUID = -5532328671712318161L;
92         private final int expectedErr;
NativeConnectException(String method, int expectedErr)93         NativeConnectException(String method, int expectedErr) {
94             super(method + "(..) failed: " + ERRORS[-expectedErr]);
95             this.expectedErr = expectedErr;
96         }
97 
expectedErr()98         int expectedErr() {
99             return expectedErr;
100         }
101     }
102 
103     static {
104         for (int i = 0; i < ERRORS.length; i++) {
105             // This is ok as strerror returns 'Unknown error i' when the message is not known.
106             ERRORS[i] = strError(i);
107         }
108     }
109 
throwConnectException(String method, int err)110     static void throwConnectException(String method, int err)
111             throws IOException {
112         if (err == ERROR_EALREADY_NEGATIVE) {
113             throw new ConnectionPendingException();
114         }
115         if (err == ERROR_ENETUNREACH_NEGATIVE) {
116             throw new NoRouteToHostException();
117         }
118         if (err == ERROR_EISCONN_NEGATIVE) {
119             throw new AlreadyConnectedException();
120         }
121         if (err == ERRNO_ENOENT_NEGATIVE) {
122             throw new FileNotFoundException();
123         }
124         throw new ConnectException(method + "(..) failed: " + ERRORS[-err]);
125     }
126 
newConnectionResetException(String method, int errnoNegative)127     public static NativeIoException newConnectionResetException(String method, int errnoNegative) {
128         NativeIoException exception = new NativeIoException(method, errnoNegative, false);
129         exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
130         return exception;
131     }
132 
newIOException(String method, int err)133     public static NativeIoException newIOException(String method, int err) {
134         return new NativeIoException(method, err);
135     }
136 
137     @Deprecated
ioResult(String method, int err, NativeIoException resetCause, ClosedChannelException closedCause)138     public static int ioResult(String method, int err, NativeIoException resetCause,
139                                ClosedChannelException closedCause) throws IOException {
140         // network stack saturated... try again later
141         if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
142             return 0;
143         }
144         if (err == resetCause.expectedErr()) {
145             throw resetCause;
146         }
147         if (err == ERRNO_EBADF_NEGATIVE) {
148             throw closedCause;
149         }
150         if (err == ERRNO_ENOTCONN_NEGATIVE) {
151             throw new NotYetConnectedException();
152         }
153         if (err == ERRNO_ENOENT_NEGATIVE) {
154             throw new FileNotFoundException();
155         }
156 
157         // TODO: We could even go further and use a pre-instantiated IOException for the other error codes, but for
158         //       all other errors it may be better to just include a stack trace.
159         throw newIOException(method, err);
160     }
161 
ioResult(String method, int err)162     public static int ioResult(String method, int err) throws IOException {
163         // network stack saturated... try again later
164         if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
165             return 0;
166         }
167         if (err == ERRNO_EBADF_NEGATIVE) {
168             throw new ClosedChannelException();
169         }
170         if (err == ERRNO_ENOTCONN_NEGATIVE) {
171             throw new NotYetConnectedException();
172         }
173         if (err == ERRNO_ENOENT_NEGATIVE) {
174             throw new FileNotFoundException();
175         }
176 
177         throw new NativeIoException(method, err, false);
178     }
179 
Errors()180     private Errors() { }
181 }
182