FTPSClient retrievefile() hanging - java

I'm creating an apache FTPS client (because the remote server won't allow plain FTP). I can connect and delete files without problem, but when using retrieveFile() or retrieveFileStream(), it hangs.
For some reason, very small files do transfer (up to 5792 bytes), but anything else gives the following PrintCommandListener output:
run:
220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
220-You are user number 2 of 50 allowed.
220-Local time is now 19:42. Server port: 21.
220-This is a private system - No anonymous login
220-IPv6 connections are also welcome on this server.
220 You will be disconnected after 15 minutes of inactivity.
AUTH TLS
234 AUTH TLS OK.
USER
331 User OK. Password required
PASS
230 OK. Current restricted directory is /
TYPE A
200 TYPE is now ASCII
EPSV
229 Extended Passive mode OK (|||53360|)
RETR test.txt
150-Accepted data connection
150 7.3 kbytes to download
Here is the code:
try {
FTPSClient ftpClient = new FTPSClient("tls",false);
ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
ftpClient.connect(host, port);
int reply = ftpClient.getReplyCode();
if (FTPReply.isPositiveCompletion(reply)) {
ftpClient.enterLocalPassiveMode();
ftpClient.login(username, password);
ftpClient.enterLocalPassiveMode();
FileOutputStream outputStream = new FileOutputStream(tempfile);
ftpClient.setFileType(FTPClient.ASCII_FILE_TYPE);
ftpClient.retrieveFile("test.txt", outputStream);
outputStream.close();
ftpClient.logout();
ftpClient.disconnect();
}
} catch (IOException ioe) {
System.out.println("FTP client received network error");
}
Any ideas are greatly appreciated.

Typically the FTP command sequence for FTPS connections goes (per RFC 4217) AUTH TLS, PBSZ 0, then USER, PASS, etc. Thus you might try:
FTPSClient ftpClient = new FTPSClient("tls",false);
ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
ftpClient.connect(host, port);
int reply = ftpClient.getReplyCode();
if (FTPReply.isPositiveCompletion(reply)) {
ftpClient.execPBSZ(0);
reply = ftpClient.getReplyCode();
// Check for PBSZ error responses...
ftpClient.execPROT("P");
reply = ftpClient.getReplyCode();
// Check for PROT error responses...
ftpClient.enterLocalPassiveMode();
This explictly tells the server to not buffer the data connection (PBSZ 0), and to use TLS for protecting the data transfer (PROT P).
The fact that you are able to transfer some bytes indicates that the issue is not the usual complication with firewalls/routers/NAT, which is another common FTPS issue.
Hope this helps!

Even if PBSZ 0 and PROT P are called in the correct sequence, sometimes the server does require SSL session reuse which is not the case by default for the client.
For example, the following reply comes when trying to list a directory. As a result no content listing is returned, this way the client seeing as if the directory is empty:
LIST /
150 Here comes the directory listing.
522 SSL connection failed; session reuse required: see require_ssl_reuse option in sftpd.conf man page
To overcome that, custom initialization of the FTPSClient is needed by overriding _prepareDataSocket_() method.
The solution is explained in details here: https://eng.wealthfront.com/2016/06/10/connecting-to-an-ftps-server-with-ssl-session-reuse-in-java-7-and-8/
Working code sample taken from the above link:
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Locale;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import org.apache.commons.net.ftp.FTPSClient;
import com.google.common.base.Throwables;
public class SSLSessionReuseFTPSClient extends FTPSClient {
// adapted from: https://trac.cyberduck.io/changeset/10760
#Override
protected void _prepareDataSocket_(final Socket socket) throws IOException {
if(socket instanceof SSLSocket) {
final SSLSession session = ((SSLSocket) _socket_).getSession();
final SSLSessionContext context = session.getSessionContext();
try {
final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache");
sessionHostPortCache.setAccessible(true);
final Object cache = sessionHostPortCache.get(context);
final Method putMethod = cache.getClass().getDeclaredMethod("put",Object.class, Object.class);
putMethod.setAccessible(true);
final Method getHostMethod = socket.getClass().getDeclaredMethod("getHost");
getHostMethod.setAccessible(true);
Object host = getHostMethod.invoke(socket);
final String key = String.format("%s:%s", host, String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT);
putMethod.invoke(cache, key, session);
} catch(Exception e) {
throw Throwables.propagate(e);
}
}
}
}

Hope someone finds my comment useful after several years.
In my case, I replaced retrieveFile with retrieveFileStream. It requires more code, but at least it works.

For me, I fixed the problem after upgrading Apache Commons Net to 3.8.0.
dependencies {
implementation 'commons-net:commons-net:3.8.0'
...
}

Related

Uploading a file to testcontainer FTP server fails with Connection refused after being connected

I'm working with FTPClient against an FTP server using Testcontainers.
A reproducible code sample is here:
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.images.builder.ImageFromDockerfile;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import static org.assertj.core.api.Assertions.assertThat;
#Testcontainers
class FtpUtilsTest {
private static final int PORT = 21;
private static final String USER = "user";
private static final String PASSWORD = "password";
private static final int FTP_TIMEOUT_IN_MILLISECONDS = 1000 * 60;
private static final GenericContainer ftp = new GenericContainer(
new ImageFromDockerfile()
.withDockerfileFromBuilder(builder ->
builder
.from("delfer/alpine-ftp-server:latest")
.build()
)
)
.withExposedPorts(PORT)
.withEnv("USERS", USER + "|" + PASSWORD);
#BeforeAll
public static void staticSetup() throws IOException {
ftp.start();
}
#AfterAll
static void afterAll() {
ftp.stop();
}
#Test
void test() throws IOException {
FTPClient ftpClient = new FTPClient();
ftpClient.setDataTimeout(FTP_TIMEOUT_IN_MILLISECONDS);
ftpClient.setConnectTimeout(FTP_TIMEOUT_IN_MILLISECONDS);
ftpClient.setDefaultTimeout(FTP_TIMEOUT_IN_MILLISECONDS);
// Log
ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));
// Connect
try {
ftpClient.connect("localhost", ftp.getMappedPort(PORT));
ftpClient.setSoTimeout(FTP_TIMEOUT_IN_MILLISECONDS);
int reply = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftpClient.disconnect();
throw new AssertionError();
}
// Login
boolean loginSuccess = ftpClient.login(USER, PASSWORD);
if (!loginSuccess) {
throw new AssertionError();
}
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.enterLocalPassiveMode();
} catch (IOException e) {
throw new AssertionError(e);
}
String remoteFile = "fileonftp";
try (InputStream targetStream = new ByteArrayInputStream("Hello FTP".getBytes())) {
assertThat(ftpClient.isConnected()).isTrue();
ftpClient.storeFile(remoteFile, targetStream);
}
}
}
This prints:
220 Welcome Alpine ftp server https://hub.docker.com/r/delfer/alpine-ftp-server/
USER *******
331 Please specify the password.
PASS *******
230 Login successful.
TYPE I
200 Switching to Binary mode.
PASV
227 Entering Passive Mode (172,17,0,3,82,15).
[Replacing PASV mode reply address 172.17.0.3 with 127.0.0.1]
then fails with:
Connection refused (Connection refused)
java.net.ConnectException: Connection refused (Connection refused)
...
at java.base/java.net.Socket.connect(Socket.java:609)
at org.apache.commons.net.ftp.FTPClient._openDataConnection_(FTPClient.java:866)
at org.apache.commons.net.ftp.FTPClient._storeFile(FTPClient.java:1053)
at org.apache.commons.net.ftp.FTPClient.storeFile(FTPClient.java:3816)
at org.apache.commons.net.ftp.FTPClient.storeFile(FTPClient.java:3846)
What I don't understand is that it fails after successfully connecting and logging in, and returning true for isConnected.
Turns out that when removing the ftpClient.enterLocalPassiveMode(); it works, but I need it to work with the passive mode.
I guess the failure is related when switching to a different port for the passive call.
but when trying to add the ports to the withExposedPorts the container fails to start with:
Caused by: org.testcontainers.containers.ContainerLaunchException: Timed out waiting for container port to open (localhost ports: [55600, 55601, 55602, 55603, 55604, 55605, 55606, 55607, 55608, 55609, 55598, 55599] should be listening)
Running against a docker (docker run -d -p 21:21 -p 21000-21010:21000-21010 -e USERS="user|password" delfer/alpine-ftp-server) works.
Local docker versions:
Docker version 20.10.11, build dea9396
Docker Desktop 4.3.1
Testcontainers - appears to behave the same both on 1.16.2 and 1.15.3
Link to testcontainers discussion
As you already figured out in the comments, the tricky part about FTP passive mode is that the server uses another port (not 21) for communication.
In the docker image you're using, it's a port from the 21000-21010 range by default. So you need to publish (expose) these additional container ports. In docker run command you used -p 21000-21010:21000-21010 for that.
However, Testcontainers library is designed to publish to random host ports to avoid the problem, when a desired fixed port (or a range of ports) is already occupied on the host side.
In case of FTP passive mode random ports on the host side cause problems, because afaik you can't instruct the ftp client to override the port, which FTP server returned for the passive mode. You'd need something like ftpClient.connect("localhost", ftp.getMappedPort(PORT)); but for passive mode ports as well.
Therefore the only solution I see here is to use a FixedHostPortContainer. Even though it's marked as deprecated and not recommended to use because of the mentioned issues with occupied ports, I think this is a valid use case for it here. FixedHostPortGenericContainer allows to publish fixed ports on the host side. Something like:
private static final int PASSIVE_MODE_PORT = 21000;
...
private static final FixedHostPortGenericContainer ftp = new FixedHostPortGenericContainer<>(
"delfer/alpine-ftp-server:latest")
.withFixedExposedPort(PASSIVE_MODE_PORT, PASSIVE_MODE_PORT)
.withExposedPorts(PORT)
.withEnv("USERS", USER + "|" + PASSWORD)
.withEnv("MIN_PORT", String.valueOf(PASSIVE_MODE_PORT))
.withEnv("MAX_PORT", String.valueOf(PASSIVE_MODE_PORT));
Keep in mind that this solution relies on the assumption that 21000 port is always free. If you're going to run this in the environment where it's not guaranteed, then you need to tweak it to find a free host port first. Like:
private static FixedHostPortGenericContainer ftp = new FixedHostPortGenericContainer<>(
"delfer/alpine-ftp-server:latest")
.withExposedPorts(PORT)
.withEnv("USERS", USER + "|" + PASSWORD);
#BeforeAll
public static void staticSetup() throws Exception {
Integer freePort = 0;
try (ServerSocket socket = new ServerSocket(0)) {
freePort = socket.getLocalPort();
}
ftp = (FixedHostPortGenericContainer)ftp.withFixedExposedPort(freePort, freePort)
.withEnv("MIN_PORT", String.valueOf(freePort))
.withEnv("MAX_PORT", String.valueOf(freePort));
ftp.start();
}
An answer similar to the accepted one but without using deprecated functionalities.
Note that we still have to use the fixed port 21000.
public static class FTPContainer extends GenericContainer<FTPContainer>
{
private static FTPContainer container;
private FTPContainer()
{
super(DockerImageName.parse("delfer/alpine-ftp-server:latest"));
}
#SuppressWarnings("resource")
public static FTPContainer getInstance()
{
if (container == null)
{
container = new FTPContainer().withEnv("USERS", "test|test|/home/").withExposedPorts(21)
.withCreateContainerCmdModifier(e -> e.getHostConfig()
.withPortBindings(new PortBinding(Ports.Binding.bindPort(21000), new ExposedPort(21000))))
.withEnv("MIN_PORT", "21000").withEnv("MAX_PORT", "21000");
}
return container;
}
#Override
public void start()
{
super.start();
}
#Override
public void stop()
{
// Handled when JVM stops
}
}
And then you can use your FTP server instance like
FTPContainer FTP = FTPContainer.getInstance();
FTP.start();

Java Socket not using proxy from JAVA_TOOL_OPTIONS

How to make all Java connections to use proxy provided via JAVA_TOOL_OPTIONS environment variable?
The simple app I'm using as a test is taken from GitHub:
package to.noc.sslping;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.OutputStream;
public class SSLPing {
public static void main(String[] args) {
if (args.length != 2) {
System.out.println("Usage: java -jar SSLPing.jar <host> <port>");
System.exit(1);
}
try {
String hostname = args[0];
int port = Integer.parseInt(args[1]);
System.out.println("About to connect to '" + hostname + "' on port " + port);
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(hostname, port);
// Hostname verification is not done by default in Java with raw SSL connections.
// The next 3 lines enable it.
SSLParameters sslParams = new SSLParameters();
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
sslsocket.setSSLParameters(sslParams);
// we only send 1 byte, so don't buffer
sslsocket.setTcpNoDelay(true);
// Write a test byte to trigger the SSL handshake
OutputStream out = sslsocket.getOutputStream();
out.write(1);
// If no exception happened, we connected successfully
System.out.println("Successfully connected");
} catch (Exception e) {
e.printStackTrace();
}
}
}
What I want is to be able to provide the PROXY settings via environment variables without having to configure it in the Java code.
I found that it is possible to provide some settings via the JAVA_TOOL_OPTIONS env.
JAVA_TOOL_OPTIONS="-Dhttp.proxyHost=161.xxx.xxx.xxx
-Dhttp.proxyPort=8080
-Dhttp.proxySet=true
-Dhttps.proxyHost=161.xxx.xxx.xxx
-Dhttps.proxyPort=8080
-Dhttps.proxySet=true"
It is correctly seen by the command
java -jar SSLPing.jar google.com 443
Picked up JAVA_TOOL_OPTIONS: -Dhttp.proxyHost=161.xxx.xxx.xxx
-Dhttp.proxyPort=8080
-Dhttp.proxySet=true
-Dhttps.proxyHost=161.xxx.xxx.xxx
-Dhttps.proxyPort=8080
-Dhttps.proxySet=true
About to connect to 'google.com' on port 443
Successfully connected
However when I need to reach a particular URL that requires the proxy, it fails to connect.
How do I make any socket to use the proxy from JAVA_TOOL_OPTIONS env?
How to check if the sockets are using the proxy?

How to check whether current LDAP server is up or down before doing an authentication

I want to check whether the current LDAP server is up or down before doing an authentication. I am using UnboundID LDAP SDK. IS there any possible way to do this?
Check if the port is up and running. It's pur Java. No SSL Factory or SSL Context or Credentials needed. Has a time out in milliseconds, if the package is dropped by a firewall.
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
public static boolean isPortReachable(String host, int port, int timeOutMS) {
try (Socket serverSocket = new Socket();) {
serverSocket.connect(new InetSocketAddress(host, port), timeOutMS);
return true;
} catch (final IOException e) { /* Ignore, Port not reachable */ }
return false;
}
Usage for LDAP or LDAPS:
if (isPortReachable(hostname, 389, 200)) { ... }
if (isPortReachable(hostname, 636, 200)) { ... }
There is a method isConnected():
public boolean isConnected()
Indicates whether this connection is currently established.
Returns:
true if this connection is currently established, or false if it is not.
So a simple example something like:
LDAPConnection ldc = new LDAPConnection()
ldc.connect(...);
if (ldc.isConnected())
{
do good stuff
}
else
{
getLDAPConnection(...);
}
Should do the trick.
The best way to test whether any resource is available is simply to try to use it in the normal course of your application, and cope with the errors as they arise.
Otherwise you're trying to predict the future.
We can do this with SSUtil. Here an authenticated entry will not be needed and we can check whether the server is up or down before doing any authentication.
SSLUtil su = new SSLUtil(new TrustAllTrustManager());
SSLSocketFactory sf = su.createSSLSocketFactory();
LDAPConnection connection = new LDAPConnection(sf,"localhost", 10636);
Looking for pros and cons for this solution.

HttpClient: How to have only one connection to the server?

This code creates a new connection to the RESTful server for each request rather than just use the existing connection. How do I change the code, so that there is only one connection?
The line "response = oClientCloseable.execute(...)" not only does the task, but creates a connection.
I checked the server daemon log and the only activity generates from the .execute() method.
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
...
String pathPost = "http://someurl";
String pathDelete = "http://someurl2";
String xmlPost = "myxml";
HttpResponse response = null;
BufferedReader rd = null;
String line = null;
CloseableHttpClient oClientCloseable = HttpClientBuilder.create().setDefaultRequestConfig(defaultRequestConfig).build();
for (int iLoop = 0; iLoop < 25; iLoop++)
{
HttpPost hPost = new HttpPost(pathPost);
hPost.setHeader("Content-Type", "application/xml");
StringEntity se = new StringEntity(xmlPost);
hPost.setEntity(se);
line = "";
try
{
response = oClientCloseable.execute(hPost);
rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
while ((line = rd.readLine()) != null)
{
System.out.println(line);
}
}
catch (ClientProtocolException e)
{
e.printStackTrace();
}
catch (ConnectionPoolTimeoutException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
HttpClientUtils.closeQuietly(response);
}
HttpDelete hDelete = new HttpDelete(pathDelete);
hDelete.setHeader("Content-Type", "application/xml");
try
{
response = oClientCloseable.execute(hDelete);
}
catch (ClientProtocolException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
HttpClientUtils.closeQuietly(response);
}
}
oClientCloseable.close();
The server daemon log emits the following for whatever it is worth, when connecting.
HTTP connection from [192.168.20.86]...ALLOWED
POST [/linx] SIZE 248
LINK-18446744073709551615: 2 SEND-BMQs, 2 RECV-BMQs
THREAD-LINK_CONNECT-000, TID: 7F0F1B7FE700 READY
NODE connecting to [192.168.30.20]:9099...
LINK-0-CONTROL-NODE-0 connected to 192.168.30.20(192.168.30.20 IPv4 address: 192.168.30.20):9099
Auth accepted, protocol compatible
NODE connecting to [192.168.30.20]:9099...
This article seems the most relevant, as it talks about consuming (closing) connections, which ties in the response. That article is also out of date, as consumeContent is deprecated. It seems that response.close() is the proper way, but that closes the connection and a new response creates a new connection.
It seems that I need to somehow create one response to the serer daemon and then change action (get, post, put, or delete).
Thoughts on how the code should change?
Here are some other links that I used:
link 1
link 2
link 3
I implemented the suggestion of Robert Rowntree (sorry not sure to properly reference name) by replacing the beginning code with:
// Increase max total connection to 200 and increase default max connection per route to 20.
// Configure total max or per route limits for persistent connections
// that can be kept in the pool or leased by the connection manager.
PoolingHttpClientConnectionManager oConnectionMgr = new PoolingHttpClientConnectionManager();
oConnectionMgr.setMaxTotal(200);
oConnectionMgr.setDefaultMaxPerRoute(20);
oConnectionMgr.setMaxPerRoute(new HttpRoute(new HttpHost("192.168.20.120", 8080)), 20);
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setSocketTimeout(5000)
.setConnectTimeout(5000)
.setConnectionRequestTimeout(5000)
.setStaleConnectionCheckEnabled(true)
.build();
//HttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(defaultRequestConfig).build();
CloseableHttpClient oClientCloseable = HttpClientBuilder.create()
.setConnectionManager(oConnectionMgr)
.setDefaultRequestConfig(defaultRequestConfig)
.build();
I still saw the bunch of authenticates.
I contacted the vendor and shared with them the log using the modified version and my code was clean.
My test sample created a connection (to a remote server) followed by deleting the connection and repeating however many times. Their code dumps the authenticate message each time a connection creation request arrives.
I was pointed to what technically I already knew that the line that creates a new RESTful connection to the service is always "XXXXX connection allowed". There was one of those, two if you count my going to the browser based interface afterwards to make sure that all my links were gone.
Sadly, I am not sure that I can use the Apache client, so sad. Apache does not support message bodies inside a GET request. To the simple minded here (me, in this case), Apache does not allow:
GET http://www.example.com/whatevermethod:myport?arg1=data1&arg2=data2
Apache HttpClient --> HttpGet does not have a setEntities command. Research showed that as a POST request, but the service is the way that it is and will not change, so...
You can definitely use query parameters in Apache HttpClient:
URIBuilder builder = new URIBuilder("http://www.example.com/whatevermehtod");
builder.addParameter("arg1", "data1");
URI uri = builder.build();
HttpGet get = new HttpGet(uri);

How to connect to gtalk in java?

i am trying to code a small XMPP gtalk client in java. I know there is a lot of libraries that help you that but the RFC is so easy to understand that i decide to write a client by myself.
I know that the gtalk server is talk.google.com:5222 but when i try this small program i get this result :
HTTP/1.1 302 Found
Location: http://www.google.com/talk/
Content-Type: text/html
Content-Length: 151
<HTML><HEAD><TITLE>302 Moved</TITLE></HEAD><BODY><H1>302 Moved</H1>The document has moved here.</BODY></HTML>
I also tried to connect the location specified but it doesn't work. Here is my code in java :
package fr.grosdim.myjabber;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSocketFactory;
/**
* Hello world!
*
*/
public class App {
public static void main(String[] args) {
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory
.getDefault();
try {
Socket s = new Socket("talk.google.com", 5222);
PrintWriter out = new PrintWriter(s.getOutputStream());
out.println("<?xml version=\\'1.0\\' encoding=\\'utf-8\\' ?>");
out
.println("<stream:stream to='talk.google.com:5222' "
+ "xmlns='jabber:client'"
+ " xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>");
out.flush();
BufferedReader reader = new BufferedReader(new InputStreamReader(s
.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
out.println("</stream>");
s.close();
} catch (SSLPeerUnverifiedException e) {
System.out.println(" Erreur d'auth :" + e.getLocalizedMessage());
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}
How can i connect to the gtalk server?
XMPP isn't a trivial protocol to implement, and I don't think you'll get very far by sending hand-crafted XML strings to the server.
I'd recommend studying some existing source code.
Spark and OpenFire are one example of a nice open source XMPP client and server implementation in java.
You might try getting OpenFire running locally in a debugger (or with verbose logging turned on) so you can get an idea of what it's doing with your packets.
Although not directly related, you may need a server to test against and one for which you can see the source. I suggest that you look at what the Vysper guys are doing http://mina.apache.org/vysper/
You have several problems with your code, not counting the stylistic one of not using a DOM before sending (which is a best practice in the XMPP world).
You need to connect to "talk.l.google.com". See the results of "dig +short _xmpp-client._tcp.gmail.com SRV" on the command line to find out what servers to connect to.
In your XML prolog, you're double escaping the single quotes, which will actually send a backslash.
The to attribute in your stream:stream should be "gmail.com", without the port number.
All of that being said, I'll second the other posters with a plea for you to not start another Java client library, but to pitch in on an existing one.
Why are you writing an XML version before writing the stream stanza? The server is expecting a stream of defined format, and not an XML structure. Remove this line
"out.println("< ? xml version=\\'1.0\\' encoding=\\'utf-8\\' ?>")"
then it will work for sure.

Categories