0

My WiFi Samsung air conditioner doesn't respond in a standard XML format. Is there a way to maybe ignore the first response?

If I open a SSL connection it responds with two lines:

DRC-1.00 
<?xml version="1.0" encoding="utf-8" ?><Update Type="InvalidateAccount"/>

To which I have to respond with:

<?xml version="1.0" encoding="utf-8" ?><Request Type="GetToken" />

The stack trace in Android is:

W/System.err﹕ java.net.ProtocolException: Unexpected status line: DRC-1.00

My code:

    URL url;
    URLConnection conn = null;

    try {
        //Create connection
        url = new URL(targetURL);

        // for not trusted ssl connections (https)
        FakeX509TrustManager.allowAllSSL();
        Log.v(TAG, "Set \"javax.net.debug\" to \"all\"");
        System.setProperty("javax.net.debug", "all");

        conn = url.openConnection();
        conn.setReadTimeout(5000 /* milliseconds */);
        conn.setConnectTimeout(5000 /* milliseconds */);
        conn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
        conn.setUseCaches(false);
        conn.setDoInput(true);
        conn.setDoOutput(true);

        //Send request
        OutputStream os = conn.getOutputStream();
        BufferedWriter writer = new BufferedWriter(
                new OutputStreamWriter(os, "UTF-8"));
        writer.flush();
        writer.close();
        os.close();

        Log.i(TAG, "Response " + conn.toString());

        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        XMLReader xmlReader = parser.getXMLReader();
        xmlReader.parse(new InputSource(conn.getInputStream())); // Error here

        Log.v(TAG, xmlReader.toString());



    } catch (IOException e) {
        e.printStackTrace();
    } catch (SAXException e) {
        e.printStackTrace();
    } catch (ParserConfigurationException e) {
        e.printStackTrace();
    }
}
RoadXY
  • 447
  • 4
  • 10

2 Answers2

1

The problem is not the XML. The problem is that the Samsung server is not talking real HTTP.

The (old) HTTP RFC defines how a response has to look like:

6.1 Status-Line

The first line of a Response message is the Status-Line, consisting
of the protocol version followed by a numeric status code and its
associated textual phrase, with each element separated by SP
characters. No CR or LF is allowed except in the final CRLF sequence.

Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF

"DRC-1.00" is clearly not a status-line like "HTTP/1.1 200 OK" and causes some strictly HTTP conforming parser to fail.

You have two options to solve the issue:

  • Use a Socket directly. A basic HTTP client for such a restricted usecase isn't complicated to implement. You're essentially just writing "GET path/from/url" into the socket, then read the reply. Simple java http client no server response contains a tiny example. To support Https, AFAIK all you need to do is to use an SSLSocket instead.
  • Use a http library that can be configured to work with custom protocols. How to parse a none standard HTTP response? looks promising.
Community
  • 1
  • 1
zapl
  • 63,179
  • 10
  • 123
  • 154
1

Ended up with:

From: http://blog.alcor.se/index.php/2013/08/09/using-java-to-connect-with-sslsocket-trusting-all-certificates/

try {
    // Create a trust manager that does not validate certificate chains
    TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
        @Override
        public void checkClientTrusted(java.security.cert.X509Certificate[]
                                               chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(java.security.cert.X509Certificate[]
                                               chain, String authType) throws CertificateException {
        }

        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }
    };

    // Install the all-trusting trust manager
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());

    SSLSocketFactory sslsocketfactory = sc.getSocketFactory();
    sslsocket = (SSLSocket) sslsocketfactory.createSocket(targetIp, port);
    sslsocket.setKeepAlive(true);
    // Sets this socket's read timeout in milliseconds.
    // sslsocket.setSoTimeout(timeoutSocket);

    Log.d(TAG, "Sending: " + xmlAction + xmlValue + XmlConstants.NEW_LINE);
    String line;
    ArrayList<String> receivedLines = new ArrayList<String>();

    DataOutputStream outToServerSSL = new DataOutputStream(sslsocket.getOutputStream());
    BufferedReader inFromServerSSL = new BufferedReader(new InputStreamReader(sslsocket.getInputStream()));
    outToServerSSL.writeBytes(xmlAction + xmlValue + XmlConstants.NEW_LINE + XmlConstants.NEW_LINE);

    while ((line = inFromServerSSL.readLine()) != null) {
        receivedLines.add(line);
        Log.d(TAG, "Received: " + line);
    }

    Log.d(TAG, "Closing tha sizzle");
    inFromServerSSL.close();
    outToServerSSL.close();

    return ParseXmlForStatusCode(receivedLines);

} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
} catch (UnknownHostException e) {
    e.printStackTrace();
} catch (KeyManagementException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (sslsocket != null) {
        try {
            sslsocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
General Grievance
  • 4,555
  • 31
  • 31
  • 45
RoadXY
  • 447
  • 4
  • 10