embulk-input-ftp 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 84b6cf71034980db6a9d4f78d5affdaae19aa792
4
- data.tar.gz: 104382478ca9b24c2c08cc839637083a7540b715
3
+ metadata.gz: 4777eb105f91266e7be9329f4fe7c275e3aa4753
4
+ data.tar.gz: 1d797e96b71babe96627dd68291d7b8d2d046106
5
5
  SHA512:
6
- metadata.gz: c98936c59b2c4ea47cb394462a6ab5df1a2d9fa1f4223d2c26ac9d75efde5746dac49267a668bd95657c5b19a797195a573a9984d7cdb6964725fc74cc1ade18
7
- data.tar.gz: 035fbcd64345982d6370c3e38c607121e5b97a27daf26daf9274f4de441f5a10c4a61fa667120f6d0bbed0d2854a4983c3bb701428cd12d1fb832d4e7b84ddcc
6
+ metadata.gz: aa87efee4fb5dc309366b4394a958340941f704dbd03e1cbdf4c5a29419b70a87422163c8e82e5b7d4bc2b18d1a6cd6ad1eba62aa626aab9f8c12ef8703fe03a
7
+ data.tar.gz: 2794aec32f1da6644d305d585d1fda1e18a60325d1c3dc561b6b6e5767c471a21c8c0e215d363667afcdbfa325c4beebc8ea6bcd4366691422c1ed1a970777b5
data/ChangeLog CHANGED
@@ -1,9 +1,15 @@
1
1
 
2
+ Release 0.1.2 - 2015-06-10
3
+
4
+ * Added support for SSL certificate verification
5
+
6
+
2
7
  Release 0.1.1 - 2015-04-29
3
8
 
4
9
  * Added support for SSL
5
10
  * Fixed last_path handling
6
11
 
12
+
7
13
  Release 0.1.0 - 2015-04-29
8
14
 
9
15
  * First release
data/README.md CHANGED
@@ -16,10 +16,26 @@
16
16
  - **passive_mode**: use passive mode (boolean, default: true)
17
17
  - **ascii_mode**: use ASCII mode instead of binary mode (boolean, default: false)
18
18
  - **ssl**: use FTPS (SSL encryption). (boolean, default: false)
19
- - Currently, it doesn't support certificate checking. Any certificate given by the remote host is trusted.
19
+ - **ssl_verify**: verify the certification provided by the server. By default, connection fails if the server certification is not signed by one the CAs in JVM's default trusted CA list. (boolean, default: true)
20
+ - **ssl_verify_hostname**: verify server's hostname matches with provided certificate. (boolean, default: true)
21
+ - **ssl_trusted_ca_cert_file**: if the server certification is not signed by a certificate authority, set path to the X.508 certification file (pem file) of a private CA (string, optional)
22
+ - **ssl_trusted_ca_cert_data**: similar to `ssl_trusted_ca_cert_file` but embed the contents of the PEM file as a string value instead of path to a local file (string, optional)
20
23
 
21
24
  ## Example
22
25
 
26
+ Simple FTP:
27
+
28
+ ```yaml
29
+ in:
30
+ type: ftp
31
+ host: ftp.example.net
32
+ port: 21
33
+ user: anonymous
34
+ path_prefix: /ftp/file/path/prefix
35
+ ```
36
+
37
+ FTPS encryption without server certificate verification:
38
+
23
39
  ```yaml
24
40
  in:
25
41
  type: ftp
@@ -28,6 +44,37 @@ in:
28
44
  user: anonymous
29
45
  password: "mypassword"
30
46
  path_prefix: /ftp/file/path/prefix
47
+
48
+ ssl: true
49
+ ssl_verify: false
50
+ ```
51
+
52
+ FTPS encryption with server certificate verification:
53
+
54
+ ```yaml
55
+ in:
56
+ type: ftp
57
+ host: ftp.example.net
58
+ port: 21
59
+ user: anonymous
60
+ password: "mypassword"
61
+ path_prefix: /ftp/file/path/prefix
62
+
63
+ ssl: true
64
+ ssl_verify: true
65
+
66
+ ssl_verify_hostname: false # to disable server hostname verification (optional)
67
+
68
+ # if the server use self-signed certificate, or set path to the pem file (optional)
69
+ ssl_trusted_ca_cert_file: /path/to/ca_cert.pem
70
+
71
+ # or embed contents of the pem file here (optional)
72
+ ssl_trusted_ca_cert_data: |
73
+ -----BEGIN CERTIFICATE-----
74
+ MIIFV...
75
+ ...
76
+ ...
77
+ -----END CERTIFICATE-----
31
78
  ```
32
79
 
33
80
  ## Build
data/build.gradle CHANGED
@@ -12,12 +12,13 @@ configurations {
12
12
  provided
13
13
  }
14
14
 
15
- version = "0.1.1"
15
+ version = "0.1.2"
16
16
 
17
17
  dependencies {
18
- compile "org.embulk:embulk-core:0.6.5"
19
- provided "org.embulk:embulk-core:0.6.5"
18
+ compile "org.embulk:embulk-core:0.6.8"
19
+ provided "org.embulk:embulk-core:0.6.8"
20
20
  compile files("libs/ftp4j-1.7.2.jar")
21
+ compile "org.bouncycastle:bcpkix-jdk15on:1.52"
21
22
  testCompile "junit:junit:4.+"
22
23
  }
23
24
 
@@ -9,14 +9,6 @@ import java.io.IOException;
9
9
  import java.io.InterruptedIOException;
10
10
  import java.io.InputStream;
11
11
  import java.nio.channels.Channels;
12
- import java.security.SecureRandom;
13
- import java.security.KeyManagementException;
14
- import java.security.NoSuchAlgorithmException;
15
- import java.security.cert.X509Certificate;
16
- import javax.net.ssl.SSLContext;
17
- import javax.net.ssl.SSLSocketFactory;
18
- import javax.net.ssl.TrustManager;
19
- import javax.net.ssl.X509TrustManager;
20
12
  import org.slf4j.Logger;
21
13
  import com.google.common.util.concurrent.ThreadFactoryBuilder;
22
14
  import com.google.common.collect.ImmutableList;
@@ -46,11 +38,14 @@ import org.embulk.spi.Exec;
46
38
  import org.embulk.spi.FileInputPlugin;
47
39
  import org.embulk.spi.TransactionalFileInput;
48
40
  import org.embulk.spi.util.InputStreamFileInput;
41
+ import org.embulk.spi.util.ResumableInputStream;
42
+ import org.embulk.spi.util.RetryExecutor.Retryable;
43
+ import org.embulk.spi.util.RetryExecutor.RetryGiveupException;
49
44
  import org.embulk.input.ftp.BlockingTransfer;
50
- import org.embulk.input.ftp.RetryableInputStream;
51
- import org.embulk.input.ftp.RetryExecutor.Retryable;
52
- import org.embulk.input.ftp.RetryExecutor.RetryGiveupException;
53
- import static org.embulk.input.ftp.RetryExecutor.retryExecutor;
45
+ import org.embulk.input.ftp.SSLPlugins;
46
+ import org.embulk.input.ftp.SSLPlugins.SSLPluginTask;
47
+ import org.embulk.input.ftp.SSLPlugins.SSLPluginConfig;
48
+ import static org.embulk.spi.util.RetryExecutor.retryExecutor;
54
49
 
55
50
  public class FtpFileInputPlugin
56
51
  implements FileInputPlugin
@@ -58,7 +53,7 @@ public class FtpFileInputPlugin
58
53
  private final Logger log = Exec.getLogger(FtpFileInputPlugin.class);
59
54
 
60
55
  public interface PluginTask
61
- extends Task
56
+ extends Task, SSLPlugins.SSLPluginTask
62
57
  {
63
58
  @Config("path_prefix")
64
59
  public String getPathPrefix();
@@ -97,6 +92,9 @@ public class FtpFileInputPlugin
97
92
  public List<String> getFiles();
98
93
  public void setFiles(List<String> files);
99
94
 
95
+ public SSLPluginConfig getSSLConfig();
96
+ public void setSSLConfig(SSLPluginConfig config);
97
+
100
98
  @ConfigInject
101
99
  public BufferAllocator getBufferAllocator();
102
100
  }
@@ -106,6 +104,8 @@ public class FtpFileInputPlugin
106
104
  {
107
105
  PluginTask task = config.loadConfig(PluginTask.class);
108
106
 
107
+ task.setSSLConfig(SSLPlugins.configure(task));
108
+
109
109
  // list files recursively
110
110
  List<String> files = listFiles(log, task);
111
111
  task.setFiles(files);
@@ -157,7 +157,7 @@ public class FtpFileInputPlugin
157
157
  FTPClient client = new FTPClient();
158
158
  try {
159
159
  if (task.getSsl()) {
160
- client.setSSLSocketFactory(newSSLSocketFactory(task));
160
+ client.setSSLSocketFactory(SSLPlugins.newSSLSocketFactory(task.getSSLConfig(), task.getHost()));
161
161
  client.setSecurity(FTPClient.SECURITY_FTPS);
162
162
  }
163
163
 
@@ -242,40 +242,6 @@ public class FtpFileInputPlugin
242
242
  }
243
243
  }
244
244
 
245
- private static SSLSocketFactory newSSLSocketFactory(PluginTask task)
246
- {
247
- // TODO certificate check
248
-
249
- TrustManager[] trustManager = new TrustManager[] {
250
- new X509TrustManager() {
251
- public X509Certificate[] getAcceptedIssuers()
252
- {
253
- return null;
254
- }
255
-
256
- public void checkClientTrusted(X509Certificate[] certs, String authType)
257
- {
258
- }
259
-
260
- public void checkServerTrusted(X509Certificate[] certs, String authType)
261
- {
262
- }
263
- }
264
- };
265
-
266
- try {
267
- SSLContext context = SSLContext.getInstance("TLS");
268
- context.init(null, trustManager, new SecureRandom());
269
- return context.getSocketFactory();
270
-
271
- } catch (NoSuchAlgorithmException ex) {
272
- throw new RuntimeException(ex);
273
-
274
- } catch (KeyManagementException ex) {
275
- throw new RuntimeException(ex);
276
- }
277
- }
278
-
279
245
  private List<String> listFiles(Logger log, PluginTask task)
280
246
  {
281
247
  FTPClient client = newFTPClient(log, task);
@@ -507,15 +473,15 @@ public class FtpFileInputPlugin
507
473
  return Channels.newInputStream(t.getReaderChannel());
508
474
  }
509
475
 
510
- private static class FtpRetryableOpener
511
- implements RetryableInputStream.Opener
476
+ private static class FtpInputStreamReopener
477
+ implements ResumableInputStream.Reopener
512
478
  {
513
479
  private final Logger log;
514
480
  private final FTPClient client;
515
481
  private final ExecutorService executor;
516
482
  private final String path;
517
483
 
518
- public FtpRetryableOpener(Logger log, FTPClient client, ExecutorService executor, String path)
484
+ public FtpInputStreamReopener(Logger log, FTPClient client, ExecutorService executor, String path)
519
485
  {
520
486
  this.log = log;
521
487
  this.client = client;
@@ -524,7 +490,7 @@ public class FtpFileInputPlugin
524
490
  }
525
491
 
526
492
  @Override
527
- public InputStream open(final long offset, final Exception exception) throws IOException
493
+ public InputStream reopen(final long offset, final Exception closedCause) throws IOException
528
494
  {
529
495
  try {
530
496
  return retryExecutor()
@@ -535,7 +501,7 @@ public class FtpFileInputPlugin
535
501
  @Override
536
502
  public InputStream call() throws InterruptedIOException
537
503
  {
538
- log.warn(String.format("FTP read failed. Retrying GET request with %,d bytes offset", offset), exception);
504
+ log.warn(String.format("FTP read failed. Retrying GET request with %,d bytes offset", offset), closedCause);
539
505
  return startDownload(log, client, path, offset, executor);
540
506
  }
541
507
 
@@ -603,9 +569,9 @@ public class FtpFileInputPlugin
603
569
  }
604
570
  opened = true;
605
571
 
606
- return new RetryableInputStream(
572
+ return new ResumableInputStream(
607
573
  startDownload(log, client, path, 0L, executor),
608
- new FtpRetryableOpener(log, client, executor, path));
574
+ new FtpInputStreamReopener(log, client, executor, path));
609
575
  }
610
576
 
611
577
  @Override
@@ -0,0 +1,245 @@
1
+ package org.embulk.input.ftp;
2
+
3
+ import java.util.List;
4
+ import java.io.Reader;
5
+ import java.io.FileReader;
6
+ import java.io.StringReader;
7
+ import java.io.ByteArrayInputStream;
8
+ import java.io.IOException;
9
+ import java.io.FileNotFoundException;
10
+ import java.security.KeyStore;
11
+ import java.security.NoSuchAlgorithmException;
12
+ import java.security.GeneralSecurityException;
13
+ import java.security.cert.CertificateFactory;
14
+ import java.security.cert.X509Certificate;
15
+ import java.security.cert.CertificateException;
16
+ import java.security.cert.CertificateEncodingException;
17
+ import java.security.KeyManagementException;
18
+ import javax.net.ssl.SSLSocketFactory;
19
+ import javax.net.ssl.X509TrustManager;
20
+ import com.google.common.base.Optional;
21
+ import com.google.common.base.Function;
22
+ import com.google.common.collect.ImmutableList;
23
+ import com.google.common.collect.Lists;
24
+ import com.fasterxml.jackson.annotation.JsonCreator;
25
+ import com.fasterxml.jackson.annotation.JsonProperty;
26
+ import com.fasterxml.jackson.annotation.JsonIgnore;
27
+ import org.embulk.config.Config;
28
+ import org.embulk.config.ConfigDefault;
29
+ import org.embulk.config.ConfigException;
30
+
31
+ public class SSLPlugins
32
+ {
33
+ // SSLPlugins is only for SSL clients. SSL server implementation is out ouf scope.
34
+
35
+ public interface SSLPluginTask
36
+ {
37
+ @Config("ssl_verify")
38
+ @ConfigDefault("null")
39
+ public Optional<Boolean> getSslVerify();
40
+
41
+ @Config("ssl_verify_hostname")
42
+ @ConfigDefault("true")
43
+ public boolean getSslVerifyHostname();
44
+
45
+ @Config("ssl_trusted_ca_cert_file")
46
+ @ConfigDefault("null")
47
+ public Optional<String> getSslTrustedCaCertFile();
48
+
49
+ @Config("ssl_trusted_ca_cert_data")
50
+ @ConfigDefault("null")
51
+ public Optional<String> getSslTrustedCaCertData();
52
+ }
53
+
54
+ private static enum VerifyMode
55
+ {
56
+ NO_VERIFY,
57
+ CERTIFICATES,
58
+ JVM_DEFAULT;
59
+ }
60
+
61
+ public static class SSLPluginConfig
62
+ {
63
+ static SSLPluginConfig NO_VERIFY = new SSLPluginConfig(VerifyMode.NO_VERIFY, false, ImmutableList.<byte[]>of());
64
+
65
+ private final VerifyMode verifyMode;
66
+ private final boolean verifyHostname;
67
+ private final List<X509Certificate> certificates;
68
+
69
+ @JsonCreator
70
+ private SSLPluginConfig(
71
+ @JsonProperty("verifyMode") VerifyMode verifyMode,
72
+ @JsonProperty("verifyHostname") boolean verifyHostname,
73
+ @JsonProperty("certificates") List<byte[]> certificates)
74
+ {
75
+ this.verifyMode = verifyMode;
76
+ this.verifyHostname = verifyHostname;
77
+ this.certificates = ImmutableList.copyOf(
78
+ Lists.transform(certificates, new Function<byte[], X509Certificate>() {
79
+ public X509Certificate apply(byte[] data)
80
+ {
81
+ try (ByteArrayInputStream in = new ByteArrayInputStream(data)) {
82
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
83
+ return (X509Certificate) cf.generateCertificate(in);
84
+ } catch (IOException | CertificateException ex) {
85
+ throw new RuntimeException(ex);
86
+ }
87
+ }
88
+ })
89
+ );
90
+ }
91
+
92
+ SSLPluginConfig(List<X509Certificate> certificates, boolean verifyHostname)
93
+ {
94
+ this.verifyMode = VerifyMode.CERTIFICATES;
95
+ this.verifyHostname = verifyHostname;
96
+ this.certificates = certificates;
97
+ }
98
+
99
+ static SSLPluginConfig useJvmDefault(boolean verifyHostname)
100
+ {
101
+ return new SSLPluginConfig(VerifyMode.JVM_DEFAULT, verifyHostname, ImmutableList.<byte[]>of());
102
+ }
103
+
104
+ @JsonProperty("verifyMode")
105
+ private VerifyMode getVerifyMode()
106
+ {
107
+ return verifyMode;
108
+ }
109
+
110
+ @JsonProperty("verifyHostname")
111
+ private boolean getVerifyHostname()
112
+ {
113
+ return verifyHostname;
114
+ }
115
+
116
+ @JsonProperty("certificates")
117
+ private List<byte[]> getCertData()
118
+ {
119
+ return Lists.transform(certificates, new Function<X509Certificate, byte[]>() {
120
+ public byte[] apply(X509Certificate cert)
121
+ {
122
+ try {
123
+ return cert.getEncoded();
124
+ } catch (CertificateEncodingException ex) {
125
+ throw new RuntimeException(ex);
126
+ }
127
+ }
128
+ });
129
+ }
130
+
131
+ @JsonIgnore
132
+ public X509TrustManager[] newTrustManager()
133
+ {
134
+ try {
135
+ switch (verifyMode) {
136
+ case NO_VERIFY:
137
+ return new X509TrustManager[] { getNoVerifyTrustManager() };
138
+ case CERTIFICATES:
139
+ return TrustManagers.newTrustManager(certificates);
140
+ default: // JVM_DEFAULT
141
+ return TrustManagers.newDefaultJavaTrustManager();
142
+ }
143
+ } catch (IOException | GeneralSecurityException ex) {
144
+ throw new RuntimeException(ex);
145
+ }
146
+ }
147
+ }
148
+
149
+ public static enum DefaultVerifyMode
150
+ {
151
+ VERIFY_BY_JVM_TRUSTED_CA_CERTS,
152
+ NO_VERIFY;
153
+ };
154
+
155
+ public static SSLPluginConfig configure(SSLPluginTask task)
156
+ {
157
+ return configure(task, DefaultVerifyMode.VERIFY_BY_JVM_TRUSTED_CA_CERTS);
158
+ }
159
+
160
+ public static SSLPluginConfig configure(SSLPluginTask task, DefaultVerifyMode defaultVerifyMode)
161
+ {
162
+ boolean verify = task.getSslVerify().or(defaultVerifyMode != DefaultVerifyMode.NO_VERIFY);
163
+ if (verify) {
164
+ Optional<List<X509Certificate>> certs = readTrustedCertificates(task);
165
+ if (certs.isPresent()) {
166
+ return new SSLPluginConfig(certs.get(), task.getSslVerifyHostname());
167
+ } else {
168
+ return SSLPluginConfig.useJvmDefault(task.getSslVerifyHostname());
169
+ }
170
+ } else {
171
+ return SSLPluginConfig.NO_VERIFY;
172
+ }
173
+ }
174
+
175
+ private static Optional<List<X509Certificate>> readTrustedCertificates(SSLPluginTask task)
176
+ {
177
+ String optionName;
178
+ Reader reader;
179
+ if (task.getSslTrustedCaCertData().isPresent()) {
180
+ optionName = "ssl_trusted_ca_cert_data";
181
+ reader = new StringReader(task.getSslTrustedCaCertData().get());
182
+ } else if (task.getSslTrustedCaCertFile().isPresent()) {
183
+ optionName = "ssl_trusted_ca_cert_file '" + task.getSslTrustedCaCertFile().get() + "'";
184
+ try {
185
+ reader = new FileReader(task.getSslTrustedCaCertFile().get());
186
+ } catch (IOException ex) {
187
+ throw new ConfigException("Failed to open "+optionName, ex);
188
+ }
189
+ } else {
190
+ return Optional.absent();
191
+ }
192
+
193
+ List<X509Certificate> certs;
194
+ try (Reader r = reader) {
195
+ certs = TrustManagers.readPemEncodedX509Certificates(r);
196
+ if (certs.isEmpty()) {
197
+ throw new ConfigException(optionName + " does not include valid X.509 PEM certificates");
198
+ }
199
+ } catch (CertificateException | IOException ex) {
200
+ throw new ConfigException("Failed to read "+optionName, ex);
201
+ }
202
+
203
+ return Optional.of(certs);
204
+ }
205
+
206
+ public static SSLSocketFactory newSSLSocketFactory(SSLPluginConfig config, String hostname)
207
+ {
208
+ try {
209
+ return TrustManagers.newSSLSocketFactory(
210
+ null, // TODO sending client certificate is not implemented yet
211
+ config.newTrustManager(),
212
+ config.getVerifyHostname() ? hostname : null);
213
+ } catch (KeyManagementException ex) {
214
+ throw new RuntimeException(ex);
215
+ }
216
+ }
217
+
218
+ private static class NoVerifyTrustManager
219
+ implements X509TrustManager
220
+ {
221
+ static final NoVerifyTrustManager INSTANCE = new NoVerifyTrustManager();
222
+
223
+ private NoVerifyTrustManager()
224
+ { }
225
+
226
+ @Override
227
+ public X509Certificate[] getAcceptedIssuers()
228
+ {
229
+ return null;
230
+ }
231
+
232
+ @Override
233
+ public void checkClientTrusted(X509Certificate[] certs, String authType)
234
+ { }
235
+
236
+ @Override
237
+ public void checkServerTrusted(X509Certificate[] certs, String authType)
238
+ { }
239
+ }
240
+
241
+ private static X509TrustManager getNoVerifyTrustManager()
242
+ {
243
+ return NoVerifyTrustManager.INSTANCE;
244
+ }
245
+ }
@@ -0,0 +1,276 @@
1
+ package org.embulk.input.ftp;
2
+
3
+ import java.util.List;
4
+ import java.util.ArrayList;
5
+ import java.io.File;
6
+ import java.io.FileInputStream;
7
+ import java.io.Reader;
8
+ import java.io.IOException;
9
+ import java.net.Socket;
10
+ import java.net.InetAddress;
11
+ import java.net.UnknownHostException;
12
+ import java.security.KeyStore;
13
+ import java.security.SecureRandom;
14
+ import java.security.KeyStoreException;
15
+ import java.security.KeyManagementException;
16
+ import java.security.NoSuchAlgorithmException;
17
+ import java.security.InvalidAlgorithmParameterException;
18
+ import java.security.cert.Certificate;
19
+ import java.security.cert.TrustAnchor;
20
+ import java.security.cert.PKIXParameters;
21
+ import java.security.cert.X509Certificate;
22
+ import java.security.cert.CertificateException;
23
+ import java.security.cert.CertificateParsingException;
24
+ import javax.net.ssl.SSLContext;
25
+ import javax.net.ssl.SSLSession;
26
+ import javax.net.ssl.SSLParameters;
27
+ import javax.net.ssl.TrustManager;
28
+ import javax.net.ssl.KeyManager;
29
+ import javax.net.ssl.X509TrustManager;
30
+ import javax.net.ssl.TrustManagerFactory;
31
+ import javax.net.ssl.SSLSocket;
32
+ import javax.net.ssl.SSLSocketFactory;
33
+ import javax.net.ssl.HostnameVerifier;
34
+ import org.bouncycastle.openssl.PEMParser;
35
+ import org.bouncycastle.openssl.PEMException;
36
+ import org.bouncycastle.cert.X509CertificateHolder;
37
+ import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
38
+ import sun.security.ssl.SSLSocketImpl;
39
+
40
+ public class TrustManagers
41
+ {
42
+ public static KeyStore readDefaultJavaKeyStore()
43
+ throws IOException, KeyStoreException, CertificateException
44
+ {
45
+ String path = (System.getProperty("java.home") + "/lib/security/cacerts").replace('/', File.separatorChar);
46
+ try {
47
+ KeyStore keyStore = KeyStore.getInstance("JKS");
48
+ try (FileInputStream in = new FileInputStream(path)) {
49
+ keyStore.load(in, null); // password=null because cacerts file is not encrypted
50
+ }
51
+ return keyStore;
52
+ } catch (NoSuchAlgorithmException ex) {
53
+ throw new RuntimeException(ex); // TODO assertion exception?
54
+ }
55
+ }
56
+
57
+ public static List<X509Certificate> readDefaultJavaTrustedCertificates()
58
+ throws IOException, CertificateException, KeyStoreException, InvalidAlgorithmParameterException
59
+ {
60
+ KeyStore keyStore = readDefaultJavaKeyStore();
61
+ PKIXParameters params = new PKIXParameters(keyStore);
62
+ List<X509Certificate> certs = new ArrayList<>();
63
+ for (TrustAnchor trustAnchor : params.getTrustAnchors() ) {
64
+ certs.add(trustAnchor.getTrustedCert());
65
+ }
66
+ return certs;
67
+ }
68
+
69
+ public static List<X509Certificate> readPemEncodedX509Certificates(Reader reader)
70
+ throws IOException, CertificateException
71
+ {
72
+ // this method abuses CertificateParsingException because its javadoc says
73
+ // CertificateParsingException is only for DER-encoded formats.
74
+
75
+ JcaX509CertificateConverter conv = new JcaX509CertificateConverter();
76
+ List<X509Certificate> certs = new ArrayList<>();
77
+
78
+ try {
79
+ PEMParser pemParser = new PEMParser(reader);
80
+ // PEMParser#close is unnecessary because it just closes underlying reader
81
+
82
+ while (true) {
83
+ Object pem = pemParser.readObject();
84
+
85
+ if (pem == null) {
86
+ break;
87
+ }
88
+
89
+ if (pem instanceof X509CertificateHolder) {
90
+ X509Certificate cert = conv.getCertificate((X509CertificateHolder) pem);
91
+ certs.add(cert);
92
+ }
93
+ }
94
+
95
+ } catch (PEMException ex) {
96
+ // throw when parsing PemObject to Object fails
97
+ throw new CertificateParsingException(ex);
98
+
99
+ } catch (IOException ex) {
100
+ if (ex.getClass().equals(IOException.class)) {
101
+ String message = ex.getMessage();
102
+ if (message.startsWith("unrecognised object: ")) {
103
+ // thrown at org.bouncycastle.openssl.PemParser.readObject when key type (header of a pem) is
104
+ // unknown.
105
+ throw new CertificateParsingException(ex);
106
+ } else if (message.startsWith("-----END ") && message.endsWith(" not found")) {
107
+ // thrown at org.bouncycastle.util.io.pem.PemReader.loadObject when a pem file format is invalid
108
+ throw new CertificateParsingException(ex);
109
+ }
110
+ } else {
111
+ throw ex;
112
+ }
113
+ }
114
+
115
+ return certs;
116
+ }
117
+
118
+ public static KeyStore buildKeyStoreFromTrustedCertificates(List<X509Certificate> certificates)
119
+ throws KeyStoreException
120
+ {
121
+ KeyStore keyStore = KeyStore.getInstance("JKS");
122
+ try {
123
+ keyStore.load(null);
124
+ } catch (IOException | CertificateException | NoSuchAlgorithmException ex) {
125
+ throw new RuntimeException(ex);
126
+ }
127
+ int i = 0;
128
+ for (X509Certificate cert : certificates) {
129
+ keyStore.setCertificateEntry("cert_" + i, cert);
130
+ i++;
131
+ }
132
+ return keyStore;
133
+ }
134
+
135
+ public static X509TrustManager[] newTrustManager(List<X509Certificate> trustedCertificates)
136
+ throws KeyStoreException
137
+ {
138
+ try {
139
+ TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
140
+ KeyStore keyStore = buildKeyStoreFromTrustedCertificates(trustedCertificates);
141
+ factory.init(keyStore);
142
+ List<X509TrustManager> tms = new ArrayList<>();
143
+ for (TrustManager tm : factory.getTrustManagers()) {
144
+ if (tm instanceof X509TrustManager) {
145
+ tms.add((X509TrustManager) tm);
146
+ }
147
+ }
148
+ return tms.toArray(new X509TrustManager[tms.size()]);
149
+ } catch (NoSuchAlgorithmException ex) {
150
+ throw new RuntimeException(ex); // TODO assertion exception?
151
+ }
152
+ }
153
+
154
+ public static X509TrustManager[] newDefaultJavaTrustManager()
155
+ throws IOException, CertificateException, KeyStoreException, InvalidAlgorithmParameterException
156
+ {
157
+ return newTrustManager(readDefaultJavaTrustedCertificates());
158
+ }
159
+
160
+ public static SSLContext newSSLContext(KeyManager[] keyManager, X509TrustManager[] trustManager)
161
+ throws KeyManagementException
162
+ {
163
+ try {
164
+ SSLContext context = SSLContext.getInstance("TLS");
165
+ context.init(
166
+ keyManager,
167
+ trustManager,
168
+ new SecureRandom());
169
+ return context;
170
+
171
+ } catch (NoSuchAlgorithmException ex) {
172
+ throw new RuntimeException(ex);
173
+ }
174
+ }
175
+
176
+ public static SSLSocketFactory newSSLSocketFactory(KeyManager[] keyManager, X509TrustManager[] trustManager, String verifyHostname)
177
+ throws KeyManagementException
178
+ {
179
+ SSLContext context = newSSLContext(keyManager, trustManager);
180
+ SSLSocketFactory factory = context.getSocketFactory();
181
+ if (verifyHostname == null) {
182
+ return factory;
183
+ } else {
184
+ return new VerifyHostNameSSLSocketFactory(factory, verifyHostname);
185
+ }
186
+ }
187
+
188
+ private static class VerifyHostNameSSLSocketFactory
189
+ extends SSLSocketFactory
190
+ {
191
+ private final SSLSocketFactory next;
192
+ private final String hostname;
193
+
194
+ public VerifyHostNameSSLSocketFactory(SSLSocketFactory next, String hostname)
195
+ {
196
+ this.next = next;
197
+ this.hostname = hostname;
198
+ }
199
+
200
+ @Override
201
+ public String[] getDefaultCipherSuites()
202
+ {
203
+ return next.getDefaultCipherSuites();
204
+ }
205
+
206
+ @Override
207
+ public String[] getSupportedCipherSuites()
208
+ {
209
+ return next.getSupportedCipherSuites();
210
+ }
211
+
212
+ @Override
213
+ public Socket createSocket(Socket s, String host, int port, boolean autoClose)
214
+ throws IOException
215
+ {
216
+ Socket sock = next.createSocket(s, host, port, autoClose);
217
+ setSSLParameters(sock, false);
218
+ return sock;
219
+ }
220
+
221
+ @Override
222
+ public Socket createSocket(String host, int port)
223
+ throws IOException, UnknownHostException
224
+ {
225
+ Socket sock = next.createSocket(host, port);
226
+ setSSLParameters(sock, false);
227
+ return sock;
228
+ }
229
+
230
+ @Override
231
+ public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
232
+ throws IOException, UnknownHostException
233
+ {
234
+ Socket sock = next.createSocket(host, port, localHost, localPort);
235
+ setSSLParameters(sock, false);
236
+ return sock;
237
+ }
238
+
239
+ @Override
240
+ public Socket createSocket(InetAddress host, int port)
241
+ throws IOException
242
+ {
243
+ Socket sock = next.createSocket(host, port);
244
+ setSSLParameters(sock, true);
245
+ return sock;
246
+ }
247
+
248
+ @Override
249
+ public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
250
+ throws IOException
251
+ {
252
+ Socket sock = next.createSocket(address, port, localAddress, localPort);
253
+ setSSLParameters(sock, true);
254
+ return sock;
255
+ }
256
+
257
+ private void setSSLParameters(Socket sock, boolean setHostname)
258
+ {
259
+ if (sock instanceof SSLSocket) {
260
+ SSLSocket s = (SSLSocket) sock;
261
+ String identAlgorithm = s.getSSLParameters().getEndpointIdentificationAlgorithm();
262
+ if (identAlgorithm != null && identAlgorithm.equalsIgnoreCase("HTTPS")) {
263
+ // hostname verification is already configured.
264
+ } else {
265
+ if (setHostname && s instanceof SSLSocketImpl) {
266
+ ((SSLSocketImpl) s).setHost(hostname);
267
+ }
268
+ SSLParameters params = s.getSSLParameters();
269
+ params.setEndpointIdentificationAlgorithm("HTTPS");
270
+ s.setSSLParameters(params);
271
+ // s.startHandshake
272
+ }
273
+ }
274
+ }
275
+ }
276
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-input-ftp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-29 00:00:00.000000000 Z
11
+ date: 2015-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -57,10 +57,12 @@ files:
57
57
  - libs/ftp4j-1.7.2.jar
58
58
  - src/main/java/org/embulk/input/FtpFileInputPlugin.java
59
59
  - src/main/java/org/embulk/input/ftp/BlockingTransfer.java
60
- - src/main/java/org/embulk/input/ftp/RetryExecutor.java
61
- - src/main/java/org/embulk/input/ftp/RetryableInputStream.java
60
+ - src/main/java/org/embulk/input/ftp/SSLPlugins.java
61
+ - src/main/java/org/embulk/input/ftp/TrustManagers.java
62
62
  - src/test/java/org/embulk/input/TestFtpFileInputPlugin.java
63
- - classpath/embulk-input-ftp-0.1.1.jar
63
+ - classpath/bcpkix-jdk15on-1.52.jar
64
+ - classpath/bcprov-jdk15on-1.52.jar
65
+ - classpath/embulk-input-ftp-0.1.2.jar
64
66
  - classpath/ftp4j-1.7.2.jar
65
67
  homepage: https://github.com/embulk/embulk-input-ftp
66
68
  licenses:
@@ -1,131 +0,0 @@
1
- // TODO copied from S3FileInputPlugin. This should be moved to org.embulk.
2
- package org.embulk.input.ftp;
3
-
4
- import java.util.concurrent.Callable;
5
- import java.util.concurrent.ExecutionException;
6
-
7
- public class RetryExecutor
8
- {
9
- public static RetryExecutor retryExecutor()
10
- {
11
- // TODO default configuration
12
- return new RetryExecutor(3, 500, 30*60*1000);
13
- }
14
-
15
- public static class RetryGiveupException
16
- extends ExecutionException
17
- {
18
- public RetryGiveupException(String message, Exception cause)
19
- {
20
- super(cause);
21
- }
22
-
23
- public RetryGiveupException(Exception cause)
24
- {
25
- super(cause);
26
- }
27
-
28
- public Exception getCause()
29
- {
30
- return (Exception) super.getCause();
31
- }
32
- }
33
-
34
- public static interface Retryable<T>
35
- extends Callable<T>
36
- {
37
- public T call()
38
- throws Exception;
39
-
40
- public boolean isRetryableException(Exception exception);
41
-
42
- public void onRetry(Exception exception, int retryCount, int retryLimit, int retryWait)
43
- throws RetryGiveupException;
44
-
45
- public void onGiveup(Exception firstException, Exception lastException)
46
- throws RetryGiveupException;
47
- }
48
-
49
- private final int retryLimit;
50
- private final int initialRetryWait;
51
- private final int maxRetryWait;
52
-
53
- private RetryExecutor(int retryLimit, int initialRetryWait, int maxRetryWait)
54
- {
55
- this.retryLimit = retryLimit;
56
- this.initialRetryWait = initialRetryWait;
57
- this.maxRetryWait = maxRetryWait;
58
- }
59
-
60
- public RetryExecutor withRetryLimit(int count)
61
- {
62
- return new RetryExecutor(count, initialRetryWait, maxRetryWait);
63
- }
64
-
65
- public RetryExecutor withInitialRetryWait(int msec)
66
- {
67
- return new RetryExecutor(retryLimit, msec, maxRetryWait);
68
- }
69
-
70
- public RetryExecutor withMaxRetryWait(int msec)
71
- {
72
- return new RetryExecutor(retryLimit, initialRetryWait, msec);
73
- }
74
-
75
- public <T> T runInterruptible(Retryable<T> op)
76
- throws InterruptedException, RetryGiveupException
77
- {
78
- return run(op, true);
79
- }
80
-
81
- public <T> T run(Retryable<T> op)
82
- throws RetryGiveupException
83
- {
84
- try {
85
- return run(op, false);
86
- } catch (InterruptedException ex) {
87
- throw new RetryGiveupException("Unexpected interruption", ex);
88
- }
89
- }
90
-
91
- private <T> T run(Retryable<T> op, boolean interruptible)
92
- throws InterruptedException, RetryGiveupException
93
- {
94
- int retryWait = initialRetryWait;
95
- int retryCount = 0;
96
-
97
- Exception firstException = null;
98
-
99
- while(true) {
100
- try {
101
- return op.call();
102
- } catch (Exception exception) {
103
- if (firstException == null) {
104
- firstException = exception;
105
- }
106
- if (!op.isRetryableException(exception) || retryCount >= retryLimit) {
107
- op.onGiveup(firstException, exception);
108
- throw new RetryGiveupException(firstException);
109
- }
110
-
111
- retryCount++;
112
- op.onRetry(exception, retryCount, retryLimit, retryWait);
113
-
114
- try {
115
- Thread.sleep(retryWait);
116
- } catch (InterruptedException ex) {
117
- if (interruptible) {
118
- throw ex;
119
- }
120
- }
121
-
122
- // exponential back-off with hard limit
123
- retryWait *= 2;
124
- if (retryWait > maxRetryWait) {
125
- retryWait = maxRetryWait;
126
- }
127
- }
128
- }
129
- }
130
- }
131
-
@@ -1,129 +0,0 @@
1
- // TODO copied from S3FileInputPlugin. This should be moved to org.embulk.
2
- package org.embulk.input.ftp;
3
-
4
- import java.io.InputStream;
5
- import java.io.IOException;
6
-
7
- public class RetryableInputStream
8
- extends InputStream
9
- {
10
- public interface Opener
11
- {
12
- public InputStream open(long offset, Exception exception) throws IOException;
13
- }
14
-
15
- private final Opener opener;
16
- protected InputStream in;
17
- private long offset;
18
- private long markedOffset;
19
-
20
- public RetryableInputStream(InputStream initialInputStream, Opener reopener)
21
- {
22
- this.opener = reopener;
23
- this.in = initialInputStream;
24
- this.offset = 0L;
25
- this.markedOffset = 0L;
26
- }
27
-
28
- public RetryableInputStream(Opener opener) throws IOException
29
- {
30
- this(opener.open(0, null), opener);
31
- }
32
-
33
- private void reopen(Exception exception) throws IOException
34
- {
35
- if (in != null) {
36
- in.close();
37
- in = null;
38
- }
39
- in = opener.open(offset, exception);
40
- }
41
-
42
- @Override
43
- public int read() throws IOException
44
- {
45
- while (true) {
46
- try {
47
- int v = in.read();
48
- offset += 1;
49
- return v;
50
- } catch (IOException | RuntimeException ex) {
51
- reopen(ex);
52
- }
53
- }
54
- }
55
-
56
- @Override
57
- public int read(byte[] b) throws IOException
58
- {
59
- while (true) {
60
- try {
61
- int r = in.read(b);
62
- offset += r;
63
- return r;
64
- } catch (IOException | RuntimeException ex) {
65
- reopen(ex);
66
- }
67
- }
68
- }
69
-
70
- @Override
71
- public int read(byte[] b, int off, int len) throws IOException
72
- {
73
- while (true) {
74
- try {
75
- int r = in.read(b, off, len);
76
- offset += r;
77
- return r;
78
- } catch (IOException | RuntimeException ex) {
79
- reopen(ex);
80
- }
81
- }
82
- }
83
-
84
- @Override
85
- public long skip(long n) throws IOException
86
- {
87
- while (true) {
88
- try {
89
- long r = in.skip(n);
90
- offset += r;
91
- return r;
92
- } catch (IOException | RuntimeException ex) {
93
- reopen(ex);
94
- }
95
- }
96
- }
97
-
98
- @Override
99
- public int available() throws IOException
100
- {
101
- return in.available();
102
- }
103
-
104
- @Override
105
- public void close() throws IOException
106
- {
107
- in.close();
108
- }
109
-
110
- @Override
111
- public void mark(int readlimit)
112
- {
113
- in.mark(readlimit);
114
- markedOffset = offset;
115
- }
116
-
117
- @Override
118
- public void reset() throws IOException
119
- {
120
- in.reset();
121
- offset = markedOffset;
122
- }
123
-
124
- @Override
125
- public boolean markSupported()
126
- {
127
- return in.markSupported();
128
- }
129
- }