]> WPIA git - gigi.git/blob - src/org/cacert/gigi/ping/SSLPinger.java
ADD: the daemon for pinging domains. (ping via email still missing)
[gigi.git] / src / org / cacert / gigi / ping / SSLPinger.java
1 package org.cacert.gigi.ping;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.net.InetSocketAddress;
7 import java.net.Socket;
8 import java.nio.ByteBuffer;
9 import java.nio.channels.SocketChannel;
10 import java.security.NoSuchAlgorithmException;
11 import java.util.Arrays;
12
13 import javax.net.ssl.SNIHostName;
14 import javax.net.ssl.SNIServerName;
15 import javax.net.ssl.SSLContext;
16 import javax.net.ssl.SSLEngine;
17 import javax.net.ssl.SSLEngineResult.Status;
18 import javax.net.ssl.SSLException;
19 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
20 import javax.net.ssl.SSLParameters;
21 import javax.security.cert.X509Certificate;
22
23 public class SSLPinger extends DomainPinger {
24
25     public static final String[] TYPES = new String[] {
26             "xmpp", "server-xmpp", "smtp", "imap"
27     };
28
29     @Override
30     public String ping(String domain, String configuration) {
31         try {
32             SocketChannel sch = SocketChannel.open();
33             String[] parts = configuration.split(":", 2);
34             sch.connect(new InetSocketAddress(domain, Integer.parseInt(parts[0])));
35             if (parts.length == 2) {
36                 switch (parts[1]) {
37                 case "xmpp":
38                     startXMPP(sch, false, domain);
39                     break;
40                 case "server-xmpp":
41                     startXMPP(sch, true, domain);
42                     break;
43                 case "smtp":
44                     startSMTP(sch);
45                     break;
46                 case "imap":
47                     startIMAP(sch);
48                     break;
49
50                 }
51             }
52             return test(sch, domain);
53         } catch (IOException e) {
54             return "Connecton failed";
55         }
56
57     }
58
59     private void startIMAP(SocketChannel sch) throws IOException {
60         Socket s = sch.socket();
61         InputStream is = s.getInputStream();
62         OutputStream os = s.getOutputStream();
63         scanFor(is, "\n");
64         os.write("ENABLE STARTTLS\r\n".getBytes());
65         os.flush();
66         scanFor(is, "\n");
67     }
68
69     private void startXMPP(SocketChannel sch, boolean server, String domain) throws IOException {
70         Socket s = sch.socket();
71         InputStream is = s.getInputStream();
72         OutputStream os = s.getOutputStream();
73         os.write(("<stream:stream to=\"" + domain + "\" xmlns=\"jabber:" + (server ? "server" : "client") + "\"" + " xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">").getBytes());
74         os.flush();
75         os.write("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>".getBytes());
76         os.flush();
77         scanFor(is, "<proceed");
78         scanFor(is, ">");
79
80     }
81
82     private void scanFor(InputStream is, String scanFor) throws IOException {
83         int pos = 0;
84         while (pos < scanFor.length()) {
85             if (is.read() == scanFor.charAt(pos)) {
86                 pos++;
87             } else {
88                 pos = 0;
89             }
90         }
91     }
92
93     private void startSMTP(SocketChannel sch) throws IOException {
94         Socket s = sch.socket();
95         InputStream is = s.getInputStream();
96         readSMTP(is);
97         s.getOutputStream().write("EHLO ssl.pinger\r\n".getBytes());
98         s.getOutputStream().flush();
99         readSMTP(is);
100         s.getOutputStream().write("HELP\r\n".getBytes());
101         s.getOutputStream().flush();
102         readSMTP(is);
103         s.getOutputStream().write("STARTTLS\r\n".getBytes());
104         s.getOutputStream().flush();
105         readSMTP(is);
106     }
107
108     private void readSMTP(InputStream is) throws IOException {
109         int counter = 0;
110         boolean finish = true;
111         while (true) {
112             char c = (char) is.read();
113             if (counter == 3) {
114                 if (c == ' ') {
115                     finish = true;
116                 } else if (c == '-') {
117                     finish = false;
118                 } else {
119                     throw new Error("Invalid smtp: " + c);
120                 }
121             }
122             if (c == '\n') {
123                 if (finish) {
124                     return;
125                 }
126                 counter = 0;
127             } else {
128                 counter++;
129             }
130         }
131     }
132
133     private String test(SocketChannel sch, String domain) {
134         try {
135             SSLContext sc = SSLContext.getDefault();
136             SSLEngine se = sc.createSSLEngine();
137             ByteBuffer enc_in = ByteBuffer.allocate(se.getSession().getPacketBufferSize());
138             ByteBuffer enc_out = ByteBuffer.allocate(se.getSession().getPacketBufferSize());
139             ByteBuffer dec_in = ByteBuffer.allocate(se.getSession().getApplicationBufferSize());
140             ByteBuffer dec_out = ByteBuffer.allocate(se.getSession().getApplicationBufferSize());
141             se.setUseClientMode(true);
142             SSLParameters sp = se.getSSLParameters();
143             sp.setServerNames(Arrays.<SNIServerName>asList(new SNIHostName(domain)));
144             se.setSSLParameters(sp);
145             se.beginHandshake();
146             enc_in.limit(0);
147             while (se.getHandshakeStatus() != HandshakeStatus.FINISHED && se.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) {
148                 switch (se.getHandshakeStatus()) {
149                 case NEED_WRAP:
150                     dec_out.limit(0);
151                     se.wrap(dec_out, enc_out);
152                     enc_out.flip();
153                     while (enc_out.remaining() > 0) {
154                         sch.write(enc_out);
155                     }
156                     enc_out.clear();
157                     break;
158                 case NEED_UNWRAP:
159                     if (enc_in.remaining() == 0) {
160                         enc_in.clear();
161                         sch.read(enc_in);
162                         enc_in.flip();
163                     }
164                     while (se.unwrap(enc_in, dec_in).getStatus() == Status.BUFFER_UNDERFLOW) {
165                         enc_in.position(enc_in.limit());
166                         enc_in.limit(enc_in.capacity());
167                         sch.read(enc_in);
168                         enc_in.flip();
169                     }
170                     enc_in.compact();
171                     enc_in.flip();
172                     break;
173                 case NEED_TASK:
174                     se.getDelegatedTask().run();
175                     break;
176                 case NOT_HANDSHAKING:
177                 case FINISHED:
178
179                 }
180
181             }
182             System.out.println("completed");
183             System.out.println(se.getSession().getCipherSuite());
184             X509Certificate[] peerCertificateChain = se.getSession().getPeerCertificateChain();
185             for (X509Certificate x509Certificate : peerCertificateChain) {
186                 System.out.println(x509Certificate.getSubjectDN().getName());
187             }
188             return PING_SUCCEDED;
189         } catch (NoSuchAlgorithmException e) {
190             e.printStackTrace();
191             return "Security failed";
192         } catch (SSLException e) {
193             e.printStackTrace();
194             return "Security failed";
195         } catch (IOException e) {
196             e.printStackTrace();
197             return "Connection closed";
198         }
199     }
200 }