Browse Source

Push some logic into BaseSocketAwareParser.

Make IRCParser extend the BSAP, and use that for creating new
sockets that respect bind IP/proxy settings.

Change-Id: I6d31b00552e9b65b8cece61de7ac94c3b099b8f4
Reviewed-on: http://gerrit.dmdirc.com/3973
Reviewed-by: Greg Holmes <greg@dmdirc.com>
Automatic-Compile: DMDirc Build Manager
changes/73/3973/3
Chris Smith 9 years ago
parent
commit
591a91c782

+ 139
- 35
src/com/dmdirc/parser/common/BaseSocketAwareParser.java View File

@@ -22,10 +22,16 @@
22 22
 
23 23
 package com.dmdirc.parser.common;
24 24
 
25
+import com.dmdirc.parser.irc.IRCAuthenticator;
26
+
25 27
 import java.io.IOException;
28
+import java.net.Inet6Address;
26 29
 import java.net.InetAddress;
30
+import java.net.InetSocketAddress;
31
+import java.net.Proxy;
27 32
 import java.net.Socket;
28 33
 import java.net.URI;
34
+import java.net.URISyntaxException;
29 35
 
30 36
 import javax.net.SocketFactory;
31 37
 
@@ -41,6 +47,9 @@ public abstract class BaseSocketAwareParser extends BaseParser {
41 47
     /** The local port that this parser's *most recently created* socket bound to. */
42 48
     private int localPort = -1;
43 49
 
50
+    /** The connection timeout, in milliseconds. */
51
+    private int connectTimeout = 5000;
52
+
44 53
     /**
45 54
      * Creates a new base parser for the specified URI.
46 55
      *
@@ -62,17 +71,21 @@ public abstract class BaseSocketAwareParser extends BaseParser {
62 71
     }
63 72
 
64 73
     /**
65
-     * Convenience method to record the local port of the specified socket.
74
+     * Gets the current connection timeout.
66 75
      *
67
-     * @param socket The socket whose port is being recorded
68
-     * @return The socket reference passed in, for convenience
76
+     * @return The connection timeout, in milliseconds.
69 77
      */
70
-    private Socket handleSocket(final Socket socket) {
71
-        // Store the socket as it might not have been bound yet
72
-        this.socket = socket;
73
-        this.localPort = socket.getLocalPort();
78
+    public int getConnectTimeout() {
79
+        return connectTimeout;
80
+    }
74 81
 
75
-        return socket;
82
+    /**
83
+     * Sets the connection timeout.
84
+     *
85
+     * @param connectTimeout The connection timeout, in milliseconds.
86
+     */
87
+    public void setConnectTimeout(final int connectTimeout) {
88
+        this.connectTimeout = connectTimeout;
76 89
     }
77 90
 
78 91
     /**
@@ -81,41 +94,132 @@ public abstract class BaseSocketAwareParser extends BaseParser {
81 94
      * @return An appropriately configured socket factory
82 95
      */
83 96
     protected SocketFactory getSocketFactory() {
84
-        return new SocketFactory() {
85
-
86
-            @Override
87
-            public Socket createSocket(final String host, final int port) throws IOException {
88
-                if (getBindIP() == null) {
89
-                    return handleSocket(new Socket(host, port));
90
-                } else {
91
-                    return handleSocket(new Socket(host, port, InetAddress.getByName(getBindIP()), 0));
97
+        return new BindingSocketFactory();
98
+    }
99
+
100
+    /**
101
+     * Stores a reference to a recently created socket.
102
+     *
103
+     * @param socket The newly created socket.
104
+     */
105
+    private void setSocket(final Socket socket) {
106
+        this.socket = socket;
107
+        this.localPort = socket.getLocalPort();
108
+    }
109
+
110
+    /**
111
+     * Allows subclasses to handle socket-related debug messages.
112
+     *
113
+     * @param message The debug message.
114
+     */
115
+    protected void handleSocketDebug(final String message) {
116
+        // Do nothing by default
117
+    }
118
+
119
+    private class BindingSocketFactory extends SocketFactory {
120
+
121
+        @Override
122
+        public Socket createSocket(final String host, final int port) throws IOException {
123
+            return createSocket(InetAddress.getByName(host), port);
124
+        }
125
+
126
+        @Override
127
+        @SuppressWarnings("resource")
128
+        public Socket createSocket(final InetAddress host, final int port) throws IOException {
129
+            checkPort(port, "server");
130
+            return getProxy() == null ? boundSocket(host, port) : proxiedSocket(host, port);
131
+        }
132
+
133
+        /**
134
+         * Creates and binds a new socket to the IP address specified by the parser. If the target
135
+         * address is an IPv6 address, the parser's {@link #getBindIPv6()} value will be used;
136
+         * otherwise, the standard {@link #getBindIP()} will be used.
137
+         *
138
+         * @param host The host to connect to.
139
+         * @param port The port to connect on.
140
+         * @return A new socket bound appropriately and connected.
141
+         */
142
+        @SuppressWarnings({"resource", "SocketOpenedButNotSafelyClosed"})
143
+        private Socket boundSocket(final InetAddress host, final int port) throws IOException {
144
+            final Socket socket = new Socket();
145
+            final String bindIp = host instanceof Inet6Address ? getBindIPv6() : getBindIP();
146
+
147
+            if (bindIp != null && !bindIp.isEmpty()) {
148
+                try {
149
+                    socket.bind(new InetSocketAddress(InetAddress.getByName(bindIp), 0));
150
+                } catch (IOException ex) {
151
+                    // Bind failed; continue trying to connect anyway.
152
+                    handleSocketDebug("Binding failed: " + ex.getMessage());
92 153
                 }
93 154
             }
155
+            setSocket(socket);
156
+            socket.connect(new InetSocketAddress(host, port), connectTimeout);
157
+            return socket;
158
+        }
94 159
 
95
-            @Override
96
-            public Socket createSocket(final InetAddress host, final int port) throws IOException {
97
-                if (getBindIP() == null) {
98
-                    return handleSocket(new Socket(host, port));
99
-                } else {
100
-                    return handleSocket(new Socket(host, port, InetAddress.getByName(getBindIP()), 0));
160
+        /**
161
+         * Creates a new socket via a proxy.
162
+         *
163
+         * @param host The host to connect to.
164
+         * @param port The port to connect on.
165
+         * @return A new proxy-using socket.
166
+         */
167
+        @SuppressWarnings({"resource", "SocketOpenedButNotSafelyClosed"})
168
+        private Socket proxiedSocket(final InetAddress host, final int port) throws IOException {
169
+            final URI proxy = getProxy();
170
+            final Proxy.Type proxyType = Proxy.Type.valueOf(proxy.getScheme().toUpperCase());
171
+            final String proxyHost = proxy.getHost();
172
+            final int proxyPort = checkPort(proxy.getPort(), "Proxy");
173
+
174
+            final Socket socket = new Socket(
175
+                    new Proxy(proxyType, new InetSocketAddress(proxyHost, proxyPort)));
176
+
177
+            try {
178
+                final IRCAuthenticator ia = IRCAuthenticator.getIRCAuthenticator();
179
+                final URI serverUri = new URI(null, null, host.getHostName(), port,
180
+                        null, null, null);
181
+                try {
182
+                    ia.getSemaphore().acquireUninterruptibly();
183
+                    ia.addAuthentication(serverUri, proxy);
184
+                    socket.connect(new InetSocketAddress(host, port), connectTimeout);
185
+                } finally {
186
+                    ia.removeAuthentication(serverUri, proxy);
187
+                    ia.getSemaphore().release();
101 188
                 }
189
+            } catch (URISyntaxException ex) {
190
+                // Won't happen.
102 191
             }
103 192
 
104
-            @Override
105
-            public Socket createSocket(final String host, final int port,
106
-                    final InetAddress localHost, final int localPort) throws IOException {
107
-                return handleSocket(new Socket(host, port,
108
-                        getBindIP() == null ? localHost : InetAddress.getByName(getBindIP()), localPort));
109
-            }
193
+            return socket;
194
+        }
110 195
 
111
-            @Override
112
-            public Socket createSocket(final InetAddress address,
113
-                    final int port, final InetAddress localAddress,
114
-                    final int localPort) throws IOException {
115
-                return handleSocket(new Socket(address, port,
116
-                        getBindIP() == null ? localAddress : InetAddress.getByName(getBindIP()), localPort));
196
+        /**
197
+         * Utility method to ensure a port is in the correct range. This stops networking classes
198
+         * throwing obscure exceptions.
199
+         *
200
+         * @param port The port to test.
201
+         * @param description Description of the port for error messages.
202
+         * @return The given port.
203
+         * @throws IOException If the port is out of range.
204
+         */
205
+        private int checkPort(final int port, final String description) throws IOException {
206
+            if (port > 65535 || port <= 0) {
207
+                throw new IOException(description + " port (" + port + ") is invalid.");
117 208
             }
209
+            return port;
210
+        }
211
+
212
+        @Override
213
+        public Socket createSocket(final String host, final int port,
214
+                final InetAddress localHost, final int localPort) throws IOException {
215
+            return createSocket(host, port);
216
+        }
217
+
218
+        @Override
219
+        public Socket createSocket(final InetAddress address, final int port,
220
+                final InetAddress localAddress, final int localPort) throws IOException {
221
+            return createSocket(address, port);
222
+        }
118 223
 
119
-        };
120 224
     }
121 225
 }

+ 14
- 119
src/com/dmdirc/parser/irc/IRCParser.java View File

@@ -21,7 +21,7 @@
21 21
  */
22 22
 package com.dmdirc.parser.irc;
23 23
 
24
-import com.dmdirc.parser.common.BaseParser;
24
+import com.dmdirc.parser.common.BaseSocketAwareParser;
25 25
 import com.dmdirc.parser.common.ChannelJoinRequest;
26 26
 import com.dmdirc.parser.common.ChildImplementations;
27 27
 import com.dmdirc.parser.common.CompositionState;
@@ -53,8 +53,6 @@ import java.io.IOException;
53 53
 import java.net.Inet4Address;
54 54
 import java.net.Inet6Address;
55 55
 import java.net.InetAddress;
56
-import java.net.InetSocketAddress;
57
-import java.net.Proxy;
58 56
 import java.net.Socket;
59 57
 import java.net.URI;
60 58
 import java.net.URISyntaxException;
@@ -92,7 +90,7 @@ import javax.net.ssl.X509TrustManager;
92 90
     IRCChannelInfo.class,
93 91
     IRCClientInfo.class
94 92
 })
95
-public class IRCParser extends BaseParser implements SecureParser, EncodingParser {
93
+public class IRCParser extends BaseSocketAwareParser implements SecureParser, EncodingParser {
96 94
 
97 95
     /** Max length an outgoing line should be (NOT including \r\n). */
98 96
     public static final int MAX_LINELENGTH = 510;
@@ -164,8 +162,6 @@ public class IRCParser extends BaseParser implements SecureParser, EncodingParse
164 162
     boolean post005;
165 163
     /** Has the thread started execution yet, (Prevents run() being called multiple times). */
166 164
     boolean hasBegan;
167
-    /** Connect timeout. */
168
-    private int connectTimeout = 5000;
169 165
     /** Manager used to handle prefix modes. */
170 166
     public final PrefixModeManager prefixModes = new PrefixModeManager();
171 167
     /** Manager used to handle user modes (owxis etc). */
@@ -480,24 +476,6 @@ public class IRCParser extends BaseParser implements SecureParser, EncodingParse
480 476
         addLastLine = newValue;
481 477
     }
482 478
 
483
-    /**
484
-     * Get the current Value of connectTimeout.
485
-     *
486
-     * @return The value of getConnectTimeout.
487
-     */
488
-    public int getConnectTimeout() {
489
-        return connectTimeout;
490
-    }
491
-
492
-    /**
493
-     * Set the Value of connectTimeout.
494
-     *
495
-     * @param newValue new value for value of getConnectTimeout.
496
-     */
497
-    public void setConnectTimeout(final int newValue) {
498
-        connectTimeout = newValue;
499
-    }
500
-
501 479
     /**
502 480
      * Get the current socket State.
503 481
      *
@@ -753,85 +731,6 @@ public class IRCParser extends BaseParser implements SecureParser, EncodingParse
753 731
         disconnectOnFatal = newValue;
754 732
     }
755 733
 
756
-    /**
757
-     * Create a new Socket object for the given target, using the given proxy
758
-     * if appropriate.
759
-     *
760
-     * @param target Target URI to connect to.
761
-     * @param proxy Proxy URI to use
762
-     * @return Socket, with IP Binding or Proxy as appropriate,
763
-     * @throws IOException If there was an issue creating the socket.
764
-     */
765
-    private Socket newSocket(final URI target, final URI proxy) throws IOException {
766
-        if (target.getPort() > 65535 || target.getPort() <= 0) {
767
-            throw new IOException("Server port (" + target.getPort() + ") is invalid.");
768
-        }
769
-
770
-        final Socket mySocket;
771
-        if (proxy == null) {
772
-            callDebugInfo(DEBUG_SOCKET, "Not using Proxy");
773
-            mySocket = new Socket();
774
-
775
-            final InetSocketAddress sockAddr = new InetSocketAddress(target.getHost(), target.getPort());
776
-
777
-            if (sockAddr.getAddress() instanceof Inet6Address) {
778
-                if (getBindIPv6() != null && !getBindIPv6().isEmpty()) {
779
-                    callDebugInfo(DEBUG_SOCKET, "Binding to IPv6: " + getBindIPv6());
780
-                    try {
781
-                        mySocket.bind(new InetSocketAddress(InetAddress.getByName(getBindIPv6()), 0));
782
-                    } catch (IOException e) {
783
-                        callDebugInfo(DEBUG_SOCKET, "Binding failed: " + e.getMessage());
784
-                    }
785
-                }
786
-            } else {
787
-                if (getBindIP() != null && !getBindIP().isEmpty()) {
788
-                    callDebugInfo(DEBUG_SOCKET, "Binding to IPv4: " + getBindIP());
789
-                    try {
790
-                        mySocket.bind(new InetSocketAddress(InetAddress.getByName(getBindIP()), 0));
791
-                    } catch (IOException e) {
792
-                        callDebugInfo(DEBUG_SOCKET, "Binding failed: " + e.getMessage());
793
-                    }
794
-                }
795
-            }
796
-
797
-            mySocket.connect(sockAddr, connectTimeout);
798
-        } else {
799
-            callDebugInfo(DEBUG_SOCKET, "Using Proxy");
800
-
801
-            if (getBindIP() != null && !getBindIP().isEmpty() ||
802
-                    getBindIPv6() != null && !getBindIPv6().isEmpty()) {
803
-                callDebugInfo(DEBUG_SOCKET, "IP Binding is not possible when using a proxy.");
804
-            }
805
-
806
-            final String proxyHost = proxy.getHost();
807
-            final int proxyPort = proxy.getPort();
808
-
809
-            if (proxyPort > 65535 || proxyPort <= 0) {
810
-                throw new IOException("Proxy port (" + proxyPort + ") is invalid.");
811
-            }
812
-
813
-            final Proxy.Type proxyType = Proxy.Type.valueOf(proxy.getScheme().toUpperCase());
814
-            mySocket = new Socket(new Proxy(proxyType, new InetSocketAddress(proxyHost, proxyPort)));
815
-
816
-            final IRCAuthenticator ia = IRCAuthenticator.getIRCAuthenticator();
817
-
818
-            try {
819
-                try {
820
-                    ia.getSemaphore().acquire();
821
-                } catch (InterruptedException ex) {
822
-                }
823
-
824
-                ia.addAuthentication(target, proxy);
825
-                mySocket.connect(new InetSocketAddress(target.getHost(), target.getPort()), connectTimeout);
826
-            } finally {
827
-                ia.removeAuthentication(target, proxy);
828
-                ia.getSemaphore().release();
829
-            }
830
-        }
831
-
832
-        return mySocket;
833
-    }
834
-
835 734
     /**
836 735
      * Find a socket to connect using, this will where possible attempt IPv6
837 736
      * first, before falling back to IPv4.
@@ -849,18 +748,17 @@ public class IRCParser extends BaseParser implements SecureParser, EncodingParse
849 748
      * will cause a double-length connection timeout.
850 749
      *
851 750
      * @param target Target URI to connect to.
852
-     * @param proxy Proxy URI to use
853 751
      * @return Socket to use in future connections.
854 752
      * @throws IOException if there is an error.
855 753
      */
856
-    private Socket findSocket(final URI target, final URI proxy) throws IOException {
857
-        if (proxy != null) {
754
+    private Socket findSocket(final URI target) throws IOException {
755
+        if (getProxy() != null) {
858 756
             // If we have a proxy, let it worry about all this instead.
859 757
             //
860 758
             // 1) We have no idea what sort of connectivity the proxy has
861 759
             // 2) If we do this here, then any DNS-based geo-balancing is
862 760
             //    going to be based on our location, not the proxy.
863
-            return newSocket(target, proxy);
761
+            return getSocketFactory().createSocket(target.getHost(), target.getPort());
864 762
         }
865 763
 
866 764
         URI target6 = null;
@@ -894,7 +792,7 @@ public class IRCParser extends BaseParser implements SecureParser, EncodingParse
894 792
         Exception v6Exception = null;
895 793
         if (target6 != null) {
896 794
             try {
897
-                return newSocket(target6, null);
795
+                return getSocketFactory().createSocket(target6.getHost(), target6.getPort());
898 796
             } catch (final IOException ioe) {
899 797
                 if (target4 == null) {
900 798
                     throw ioe;
@@ -906,7 +804,7 @@ public class IRCParser extends BaseParser implements SecureParser, EncodingParse
906 804
         }
907 805
         if (target4 != null) {
908 806
             try {
909
-                return newSocket(target4, null);
807
+                return getSocketFactory().createSocket(target4.getHost(), target4.getPort());
910 808
             } catch (final IOException e2) {
911 809
                 callDebugInfo(DEBUG_SOCKET, "Exception trying to use IPv4: " + e2);
912 810
                 throw e2;
@@ -936,7 +834,7 @@ public class IRCParser extends BaseParser implements SecureParser, EncodingParse
936 834
         callDebugInfo(DEBUG_SOCKET, "Connecting to " + getURI().getHost() + ':' + getURI().getPort());
937 835
 
938 836
         currentSocketState = SocketState.OPENING;
939
-        socket = findSocket(getConnectURI(getURI()), getProxy());
837
+        socket = findSocket(getConnectURI(getURI()));
940 838
 
941 839
         rawSocket = socket;
942 840
 
@@ -1063,15 +961,6 @@ public class IRCParser extends BaseParser implements SecureParser, EncodingParse
1063 961
         callDebugInfo(DEBUG_INFO, "End Thread Execution");
1064 962
     }
1065 963
 
1066
-    @Override
1067
-    public int getLocalPort() {
1068
-        if (currentSocketState == SocketState.OPENING || currentSocketState == SocketState.OPEN) {
1069
-            return socket.getLocalPort();
1070
-        } else {
1071
-            return 0;
1072
-        }
1073
-    }
1074
-
1075 964
     /** Close socket on destroy. */
1076 965
     @Override
1077 966
     protected void finalize() throws Throwable {
@@ -2340,4 +2229,10 @@ public class IRCParser extends BaseParser implements SecureParser, EncodingParse
2340 2229
     public void setCompositionState(final String host, final CompositionState state) {
2341 2230
         // Do nothing
2342 2231
     }
2232
+
2233
+    @Override
2234
+    protected void handleSocketDebug(final String message) {
2235
+        super.handleSocketDebug(message);
2236
+        callDebugInfo(DEBUG_SOCKET, message);
2237
+    }
2343 2238
 }

Loading…
Cancel
Save