I'm creating an app which generates a CSV file and some PDFs. I want my app to send those files to a server via FTPS protocol.
I'm using Apache Commons Net FTP library and it was perfectly working when I had "Require TLS session resumption on data connection when using PORT P" unchecked, but since I enabled it I can't send my files.
An error appeared :
450 TLS session of data connection has not resumed or the session does not match the control connection.
After some researches on this site I have overriden _prepareDataSocket_ in order to overcome this problem but now it just creates empty files on the server.
There is my overriden function :
#Override
protected void _prepareDataSocket_(final Socket socket) throws IOException {
if (socket instanceof SSLSocket) {
// Control socket is SSL
final SSLSession session = ((SSLSocket) _socket_).getSession();
if (session.isValid()) {
final SSLSessionContext context = session.getSessionContext();
try {
final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache");
sessionHostPortCache.setAccessible(true);
final Object cache = sessionHostPortCache.get(context);
final Method method = cache.getClass().getDeclaredMethod("put", Object.class, Object.class);
method.setAccessible(true);
method.invoke(cache, String
.format("%s:%s", socket.getInetAddress().getHostName(), String.valueOf(socket.getPort()))
.toLowerCase(Locale.ROOT), session);
method.invoke(cache, String
.format("%s:%s", socket.getInetAddress().getHostAddress(), String.valueOf(socket.getPort()))
.toLowerCase(Locale.ROOT), session);
} catch (NoSuchFieldException e) {
throw new IOException(e);
} catch (Exception e) {
throw new IOException(e);
}
} else {
throw new IOException("Invalid SSL Session");
}
}
}
and this is what FileZilla Server displays:
FileZilla Response
will this answer on another forum help?
http://forum.rebex.net/5673/450-error-connecting-to-ftp-requiring-explicit-ftp-over-tls
Related
I am building a server. I hope that after the Java server and the C# client are connected, I can send information from the HTML to the Java server, and then the Java server sends this information to the client.But I can't get the socket after the successful establishment in the service layer, so my Java server can only send fixed information to the client.
I tried using Class object = new Class(); object.setObject(socket); to save the socket, but when I call this object in the service layer, I get null;
I tried to save the socket using (Map) socket.put("socket", socket), but when I call this method in the service layer, I get null.
This is the code to make the socket. from SocketThread.java
public void run() {
ServerSocket serverSocket = null;
try{
serverSocket = new ServerSocket(5656);
LOGGER.info("socket server start, monitor 5656 port ! ");
Socket socket = serverSocket.accept();
new SocketClientRequest(socket).start();
LOGGER.info("send success ! ");
}catch (Exception ex){
LOGGER.error("send fail ! ");
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
LOGGER.error("服务器延时重启失败 ! ");
}
}
This is a method of reading the information sent by the client using the socket and sending the information to the client. from SocketClientRequest.java
public void run() {
try {
//获取socket中的数据
bufferedInputStream = new
BufferedInputStream(socket.getInputStream());
byte[] clientCharStream = new byte[messageLengthBytes];
bufferedInputStream.read(clientCharStream);
System.out.println(new String(clientCharStream, "utf-8"));
OutputStream out = socket.getOutputStream();
out.write(new String("welcome_send_server!").getBytes());
} catch (IOException e) {
LOGGER.error("read massage error [{}]", e);
}
}
Create a connection when the project starts
#EnableScheduling
#SpringBootApplication
public class GzserverApplication {
public static void main(String[] args) {
SpringApplication.run(GzserverApplication.class, args);
SocketServer socketServer = new SocketServer();
socketServer.start();
}
}
Until this step, everything is fine, but the key problem is coming.
I need to send information to the client through my controller.
this is controller
#ResponseBody
#RequestMapping(value = "firstSend)
public SResult<String> firstSend(String uName, String pNum, String time){
try{
return httpService.firstSend(uName, pNum, time);
}catch (Exception ex){
LOGGER.error(ex.getMessage(), ex);
}
return SResult.failure("fail of connect");
}
this is service
public SResult<String> firstSend(String uName, String pNum, String time) throws Exception {
String token = TokenUtil.sign(uName);
System.out.println("token code : "+token);
SocketObject socketObject = new SocketObject();
Map<String, Socket> socketMap = socketObject.socket();
Socket socket1 = socketMap.get("Socket"); // is null
Socket socket2 = socketObject.getSocket(); // is null
return SResult.success(token);
}
I hope that after the connection is successfully created, the socket can be used in the service layer, and the information is sent to the client through the socket, but no matter what I do, the socket is null in the service layer.please give me a help, thank you very much
You should not be dealing with Sockets if using Spring. Spring is a very extensive abstraction layer, that lets you avoid having to deal with the nasty details that Sockets introduce.
In your controller, you call: SocketObject socketObject = new SocketObject(); This creates a new object, presumably with a null-initialized Socket object. Nowhere in this code do you pass a socket object from the main() scope to any other scope (for example using a method named setSocket(Socket socket).
However, and I cannot stress this enough, you should not use Sockets in Spring. Think about what problem you are trying to solve, and ask yourself (why do I need to send information to the client). It is likely that Spring has a module that will do this for you in a much more scalable and manageable way.
For example, perhaps you need to establish 2-way communication between the server and the client, and need to post information to the client periodically. In this case, the WebSocket protocol (and associated Spring Websocket library) might be for you.
This is likely an XY problem. If you edit your question to illustrate the functionality you are trying to implement, it may be easier to help
So what I'm trying to do, is create unit test that checks if invoked command (on shell via ssh connection) has a proper response. The problem is that I can't read those responses. There are not many tutorials regarding Apache MINA, so I thought maybe some of you could help me out. Here's a code
#Before
public void setUpSSHd() {
sshd=SshServer.setUpDefaultServer();
sshd.setPort(22999);
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("hostkey.ser"));
sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
public boolean authenticate(String username, String password, ServerSession session) {
// TODO Auto-generated method stub
return true;
}
});
List<NamedFactory<KeyExchange>> keyExchangeFactories;
keyExchangeFactories = sshd.getKeyExchangeFactories();
sshd.setKeyExchangeFactories(keyExchangeFactories);
try {
sshd.start();
} catch (Exception e) {
e.printStackTrace();
}
}
#After
public void teardown() throws Exception { sshd.stop(); }
#Test
public void testCommands() throws Exception {
SshClient client = SshClient.setUpDefaultClient();
client.start();
ClientSession session = null;
try {
session = client.connect("localhost", 22999).await().getSession();
session.authPassword("none", "none").await().isSuccess();
System.out.println("Connection established");
final ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL);
ByteArrayOutputStream sent = new ByteArrayOutputStream();
PipedOutputStream pipedIn = new TeePipedOutputStream(sent);
channel.setIn(new PipedInputStream(pipedIn));
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream();
channel.setOut(out);
channel.setErr(err);
channel.open();
pipedIn.write("dir\r\n".getBytes());
pipedIn.flush();
channel.waitFor(ClientChannel.CLOSED, 0);
channel.close(false);
client.stop();
System.out.println(out.toString());
} catch (Exception e) {
e.printStackTrace();
fail("Cannot establish a connection");
} finally {
if (session != null)
session.close(true);
}
}
For now I simply try to print out collected response. However I get empty string everytime I try to do that. I assume there might be a problem with ssh server configuration (what shell is it supposed to use?). The best scenario would be if I could define my own commands and responses on server side and then, only check it on client side
EDIT: I've tried to manually connect to this mocked ssh server but I've got
Unable to negotiate with ::1 port 22999: no matching key exchange method found. Their offer: diffie-hellman-group1-sha1
error message.
I would suggest you to update Apache SSH. Based the source repository the version 0.5.0 is 7 years old.
using your posted code with the default JCE provider and Apache SSH
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>0.5.0</version>
<dependency>
the connect with a ssh client fails with
Their offer: diffie-hellman-group1-sha1
using a more recent Apache SSH release
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>1.4.0</version>
<dependency>
the connect is successful
This question is Extension of my previous question on this SO question "How to connect XMPP bosh server using java smack library?"
I am using Java as server side language. I have successfully implement xmpp BOSH connection using smach-jbosh thanks to #Deuteu for helping me to achieve this, so far I have modify jbosh's BOSHClient.java file and added two getter method for extracting RID and SID.
Now I have RID and SID on my app server (I am using Apache Tomcat). I need to pass this credential to Strophe (web client) so that it can attach to connection.
Here I have some doubt.
When to disconnect bosh Connection establish from the app server? before passing sid, rid and jid to strophe or after passing sid, rid and jid to strophe?
As per my observation during implementation for the same, I have observed that once bosh connection from the app server has been disconnected, session is expired and SID and RID is no longer useful!!!
I have implemented this logic (Establishing bosh connection and Extracting sid and rid) on a Servlet, here once response has been send from Servlet, Thread will get expired and end BOSH connection will get terminated, so I am not able perform `Attach()` on strophe as session is expired.
Can somebody help me with that problem?
I believe #fpsColton's answer is correct - I'm just added extra info for clarity. As requested on linked thread here is the code changes I made on this - note: I only added the parts where I've labelled "DH"
In BOSHConnection:
// DH: function to preserve current api
public void login(String username, String password, String resource)
throws XMPPException {
login(username, password, resource, false);
}
// DH: Most of this is existing login function, but added prebind parameter
// to allow leaving function after all required pre-bind steps done and before
// presence stanza gets sent (sent from attach in XMPP client)
public void login(String username, String password, String resource, boolean preBind)
throws XMPPException {
if (!isConnected()) {
throw new IllegalStateException("Not connected to server.");
}
if (authenticated) {
throw new IllegalStateException("Already logged in to server.");
}
// Do partial version of nameprep on the username.
username = username.toLowerCase().trim();
String response;
if (config.isSASLAuthenticationEnabled()
&& saslAuthentication.hasNonAnonymousAuthentication()) {
// Authenticate using SASL
if (password != null) {
response = saslAuthentication.authenticate(username, password, resource);
} else {
response = saslAuthentication.authenticate(username, resource, config.getCallbackHandler());
}
} else {
// Authenticate using Non-SASL
response = new NonSASLAuthentication(this).authenticate(username, password, resource);
}
// Indicate that we're now authenticated.
authenticated = true;
anonymous = false;
// DH: Prebind only requires connect and authenticate
if (preBind) {
return;
}
// Set the user.
if (response != null) {
this.user = response;
// Update the serviceName with the one returned by the server
config.setServiceName(StringUtils.parseServer(response));
} else {
this.user = username + "#" + getServiceName();
if (resource != null) {
this.user += "/" + resource;
}
}
// Create the roster if it is not a reconnection.
if (this.roster == null) {
this.roster = new Roster(this);
}
if (config.isRosterLoadedAtLogin()) {
this.roster.reload();
}
// Set presence to online.
if (config.isSendPresence()) {
sendPacket(new Presence(Presence.Type.available));
}
// Stores the autentication for future reconnection
config.setLoginInfo(username, password, resource);
// If debugging is enabled, change the the debug window title to include
// the
// name we are now logged-in as.l
if (config.isDebuggerEnabled() && debugger != null) {
debugger.userHasLogged(user);
}
}
and
// DH
#Override
public void disconnect() {
client.close();
}
then my Client-side (Web Server) wrapper class - for connecting from within JSP is:
Note: This is proving code rather than production - so there's some stuff in here you may not want.
public class SmackBoshConnector {
private String sessionID = null;
private String authID = null;
private Long requestID = 0L;
private String packetID = null;
private boolean connected = false;
public boolean connect(String userName, String password, String host, int port, final String xmppService) {
boolean success = false;
try {
Enumeration<SaslClientFactory> saslFacts = Sasl.getSaslClientFactories();
if (!saslFacts.hasMoreElements()) {
System.out.println("Sasl Provider not pre-loaded");
int added = Security.addProvider(new com.sun.security.sasl.Provider());
if (added == -1) {
System.out.println("Sasl Provider could not be loaded");
System.exit(added);
}
else {
System.out.println("Sasl Provider added");
}
}
BOSHConfiguration config = new BOSHConfiguration(false, host, port, "/http-bind/", xmppService);
BOSHConnection connection = new BOSHConnection(config);
PacketListener sndListener = new PacketListener() {
#Override
public void processPacket(Packet packet) {
SmackBoshConnector.this.packetID = packet.getPacketID();
System.out.println("Send PacketId["+packetID+"] to["+packet.toXML()+"]");
}
};
PacketListener rcvListener = new PacketListener() {
#Override
public void processPacket(Packet packet) {
SmackBoshConnector.this.packetID = packet.getPacketID();
System.out.println("Rcvd PacketId["+packetID+"] to["+packet.toXML()+"]");
}
};
PacketFilter packetFilter = new PacketFilter() {
#Override
public boolean accept(Packet packet) {
return true;
}
};
connection.addPacketSendingListener(sndListener, packetFilter);
connection.addPacketListener(rcvListener, packetFilter);
connection.connect();
// login with pre-bind only
connection.login(userName, password, "", true);
authID = connection.getConnectionID();
BOSHClient client = connection.getClient();
sessionID = client.getSid();
requestID = client.getRid();
System.out.println("Connected ["+authID+"] sid["+sessionID+"] rid["+requestID+"]");
success = true;
connected = true;
try {
Thread.yield();
Thread.sleep(500);
}
catch (InterruptedException e) {
// Ignore
}
finally {
connection.disconnect();
}
} catch (XMPPException ex) {
Logger.getLogger(SmackBoshConnector.class.getName()).log(Level.SEVERE, null, ex);
}
return success;
}
public boolean isConnected() {
return connected;
}
public String getSessionID() {
return sessionID;
}
public String getAuthID() {
return authID;
}
public String getRequestIDAsString() {
return Long.toString(requestID);
}
public String getNextRequestIDAsString() {
return Long.toString(requestID+1);
}
public static void main(String[] args) {
SmackBoshConnector bc = new SmackBoshConnector();
bc.connect("dazed", "i3ji44mj7k2qt14djct0t5o709", "192.168.2.15", 5280, "my.xmppservice.com");
}
}
I confess that I'm don't fully remember why I put the Thread.yield and Thread.sleep(1/2 sec) in here - I think - as you can see with added PacketListener - the lower level functions return after sending data and before getting a response back from the server - and if you disconnect before the server has sent it's response then it (also) causes it to clean up the session and things won't work. However it may be that, as #fpsColton says, this dicsonnect() isn't actually required.
Edit: I now remember a bit more about whay I included sleep() and yield(). I noticed that Smack library includes sleep() in several places, including XMPPConnection.shutdown() as per source. Plus in terms of yield() I had problems in my environment (Java in Oracle Database - probably untypical) when it wasn't included - as per Smack Forum Thread.
Good luck.
After you have created a BOSH session with smack and have extracted the SID+RID values, you need to pass them to Strophe's attach() and from here on out you need to let strophe deal with this connection. Once Strophe has attached, you do not want your server to be doing anything to the connection at all.
If your server side code sends any messages at all to the connection manager after strophe has attached, it's likely that it will send a invalid RID which will cause your session to terminate.
Again, once the session has been established and is usable by strophe, do not attempt to continue using it from the server side. After your server side bosh client completes authentication and you've passed the SID+RID to the page, just destroy the server side connection object, don't attempt to disconnect or anything as this will end your session.
The thing you need to remember is, unlike traditional XMPP connections over TCP, BOSH clients do NOT maintain a persistent connection to the server (this is why we use BOSH in web applications). So there is nothing to disconnect. The persistent connection is actually between the XMPP server and the BOSH connection manager, it's not something you need to deal with. So when you call disconnect from your server side BOSH client, you're telling the connection manager to end the session and close it's connection to the XMPP server, which completely defeats the purpose of creating the session in the first place.
I have a support website that I would like to show some stats gathered from another Java app via JMX. We have noticed the support app sometimes cannot get the stats after the other app has been restarted. I guess this is because the support app has opening a JMX connection to the other app and keeps hold of it. Then every time you go to the page to display the JMX stats it tries to gather them using the connection and it fails.
My question is, is it better to have a single JMX connection and try and work out when we should reconnect it?
Or each time we load the page with JMX stats on it should we create a new JMX connection then close it once we have the values we need?
As per my knowledge,
JMX connections are RMI Connector objects and hence can be held in the client app. + use a heartbeat approach to reconnect.
This way we can avoid overhead of re-establishing RMI connections which are not light weight.
Refer: javax.management.remote.rmi.RMIConnector
We didn't end up using a heartbeat but after reading Girish's answer came up with the following
public class JmxMetricsRetriever {
private final JMXServiceURL jmxUrl;
private final Map<String, Object> env;
private MBeanServerConnection connection;
private JmxMetricsRetriever(JMXServiceURL jmxUrl, Map<String, Object> env) {
this.jmxUrl = jmxUrl;
this.env = env;
reconnect();
}
public synchronized Object getAttributeValue(String jmxObjectName, String attributeName) {
try {
if (connection == null) {
reconnect();
}
try {
return getAttributeValuePrivate(jmxObjectName, attributeName);
} catch (ConnectException exc) {
//This is to reconnect after the Server has been restarted.
reconnect();
return getAttributeValuePrivate(jmxObjectName, attributeName);
}
} catch (MalformedObjectNameException |
AttributeNotFoundException |
MBeanException |
ReflectionException |
InstanceNotFoundException |
IOException ex) {
throw new RuntimeException(ex);
}
}
private synchronized Object getAttributeValuePrivate(String jmxObjectName, String attributeName) throws MalformedObjectNameException, MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException {
ObjectName replication = new ObjectName(jmxObjectName);
return connection.getAttribute(replication, attributeName);
}
private synchronized void reconnect() {
logger.info(String.format("Reconnecting to [%s] via JMX", jmxUrl.toString()));
try {
JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxUrl, env);
this.connection = jmxConnector.getMBeanServerConnection();
jmxConnector.connect();
} catch (IOException e) {
//Log something but don't throw an exception otherwise our app will fail to start.
}
}
public static JmxMetricsRetriever build(String url, String port, String user, String password) {
try {
JMXServiceURL jmxUrl = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + url + ":" + port + "/jmxrmi");
Map<String, Object> env = new HashMap<>();
env.put(JMXConnector.CREDENTIALS, new String[]{user, password});
return new JmxMetricsRetriever(jmxUrl, env);
} catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
}
}
When we start our app we try and create a JMX connect an hold on to it. Every time we get a JMX attribute we check the connection has been created (might not of been if the server we are connecting to was not up when we started our service). Then try and retrieve our attribute. If it failed try and reconnect and get the attribute value. We could not find a better way to test of a JMX connect was still usable so had to catch the exception.
I'm making a vysper xmpp server.
Here's my code:
public static void main(String[] args) throws Exception {
XMPPServer server = new XMPPServer("myserver.org");
StorageProviderRegistry providerRegistry = new MemoryStorageProviderRegistry();
AccountManagement accountManagement = (AccountManagement) providerRegistry.retrieve(AccountManagement.class);
Entity user = EntityImpl.parseUnchecked("user#myserver.org");
accountManagement.addUser(user, "password");
server.setStorageProviderRegistry(providerRegistry);
server.addEndpoint(new TCPEndpoint())
server.setTLSCertificateInfo(new File("keystore.jks"), "boguspw");
//server.setTLSCertificateInfo(new File("bogus_mina_tls.cert"), "boguspw");
server.start();
System.out.println("Vysper server is running...");
server.addModule(new EntityTimeModule());
server.addModule(new VcardTempModule());
server.addModule(new XmppPingModule());
server.addModule(new PrivateDataModule());
}
I've tried both certificate files. (keystore.jks,bogus_mina_tls.cert)
After I start the server, it connects to it, and tries to login but it can't login.
SmackConfiguration.setPacketReplyTimeout(5000);
config = new ConnectionConfiguration("myserver.org", port, "localhost");
config.setSelfSignedCertificateEnabled(true);
config.setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);
config.setSASLAuthenticationEnabled(true);
// config.setKeystorePath("keystore.jks");
// config.setTruststorePath("keystore.jks");
config.setKeystorePath("bogus_mina_tls.cert");
config.setTruststorePath("bogus_mina_tls.cert");
config.setTruststorePassword("boguspw");
XMPPConnection.DEBUG_ENABLED = true;
connection = new XMPPConnection(config);
try {
connection.connect();
} catch (XMPPException e) {
System.out.println("Error connect");
e.printStackTrace();
}
System.out.println("Connected: " + connection.isConnected());
try {
System.out.println(connection.isAuthenticated());
connection.login("user", "password");
} catch (XMPPException e) {
System.out.println("Error login");
e.printStackTrace();
}
I catch this exception:
SASL authentication PLAIN failed: incorrect-encoding: at
org.jivesoftware.smack.SASLAuthentication.authenticate(SASLAuthentication.java:337)
at
org.jivesoftware.smack.XMPPConnection.login(XMPPConnection.java:203)
at org.jivesoftware.smack.Connection.login(Connection.java:348) at
com.protocol7.vysper.intro.WorkingClient.init(WorkingClient.java:57)
at
com.protocol7.vysper.intro.WorkingClient.(WorkingClient.java:27)
at com.protocol7.vysper.intro.Runclient.main(Runclient.java:11)
I've seen these examples (1st, 2nd) but they don't work.
At first please note that the server certificate is not used for user authentication, it is used to provide secure communication channel between client and server.
From the log you can see that your authentication method is "SASL PLAIN", using a user and password.
On the server, you are setting username/password as:
accountManagement.addUser("user#myserver.org", "password");
but on the client you're using
connection.login("user", "password");
This doesn't fit with the error message you are posting, but I'd suggest you try again with correct user/password.