From e4dd80005c4839f74c76270b1f7c2668e8007305 Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Tue, 17 Aug 2010 14:51:39 +0000 Subject: [PATCH] gave HKP server proper error handling, going through all IPs of a pool until a response is received, reporting "too many responses", "no keys found", "insufficient query" correctly Update issue 9 Proper error handling added. --- res/values/strings.xml | 1 + .../thialfihar/android/apg/HkpKeyServer.java | 131 ++++++++++++++---- src/org/thialfihar/android/apg/KeyServer.java | 18 ++- .../android/apg/KeyServerQueryActivity.java | 19 +-- 4 files changed, 130 insertions(+), 39 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 0451ec00e..472e43770 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -194,6 +194,7 @@ No keys exported. Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used. Couldn\'t find key %08X. + Found %s key(s). diff --git a/src/org/thialfihar/android/apg/HkpKeyServer.java b/src/org/thialfihar/android/apg/HkpKeyServer.java index ce65e1da2..3cb59e701 100644 --- a/src/org/thialfihar/android/apg/HkpKeyServer.java +++ b/src/org/thialfihar/android/apg/HkpKeyServer.java @@ -3,10 +3,13 @@ package org.thialfihar.android.apg; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; -import java.net.URLConnection; import java.net.URLEncoder; +import java.net.UnknownHostException; import java.util.GregorianCalendar; import java.util.List; import java.util.Vector; @@ -16,6 +19,25 @@ import java.util.regex.Pattern; import android.text.Html; public class HkpKeyServer extends KeyServer { + private static class HttpError extends Exception { + private static final long serialVersionUID = 1718783705229428893L; + private int mCode; + private String mData; + + public HttpError(int code, String data) { + super("" + code + ": " + data); + mCode = code; + mData = data; + } + + public int getCode() { + return mCode; + } + + public String getData() { + return mData; + } + } private String mHost; private short mPort = 11371; @@ -36,29 +58,90 @@ public class HkpKeyServer extends KeyServer { mPort = port; } - @Override - List search(String query) - throws MalformedURLException, IOException { - Vector results = new Vector(); - - String url = "http://" + mHost + ":" + mPort + "/pks/lookup?op=index&search=" + - URLEncoder.encode(query, "utf8"); - URL realUrl = new URL(url); - URLConnection conn = realUrl.openConnection(); - InputStream is = conn.getInputStream(); + static private String readAll(InputStream in, String encoding) + throws IOException { ByteArrayOutputStream raw = new ByteArrayOutputStream(); byte buffer[] = new byte[1 << 16]; int n = 0; - while ((n = is.read(buffer)) != -1) { + while ((n = in.read(buffer)) != -1) { raw.write(buffer, 0, n); } - String encoding = conn.getContentEncoding(); if (encoding == null) { encoding = "utf8"; } - String data = raw.toString(encoding); + return raw.toString(encoding); + } + + private String query(String request) + throws QueryException, HttpError { + InetAddress ips[]; + try { + ips = InetAddress.getAllByName(mHost); + } catch (UnknownHostException e) { + throw new QueryException(e.toString()); + } + for (int i = 5; i < ips.length; ++i) { + try { + String url = "http://" + ips[i].getHostAddress() + ":" + mPort + request; + URL realUrl = new URL(url); + HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); + conn.connect(); + int response = conn.getResponseCode(); + if (response >= 200 && response < 300) { + return readAll(conn.getInputStream(), conn.getContentEncoding()); + } + else { + String data = readAll(conn.getErrorStream(), conn.getContentEncoding()); + throw new HttpError(response, data); + } + } catch (MalformedURLException e) { + // nothing to do, try next IP + } catch (IOException e) { + // nothing to do, try next IP + } + } + + throw new QueryException("querying server(s) for '" + mHost + "' failed"); + } + + @Override + List search(String query) + throws QueryException, TooManyResponses, InsufficientQuery { + Vector results = new Vector(); + + if (query.length() < 3) { + throw new InsufficientQuery(); + } + + String encodedQuery; + try { + encodedQuery = URLEncoder.encode(query, "utf8"); + } catch (UnsupportedEncodingException e) { + return null; + } + String request = "/pks/lookup?op=index&search=" + encodedQuery; + + String data = null; + try { + data = query(request); + } catch (HttpError e) { + if (e.getCode() == 404) { + return results; + } else { + System.out.println(e.getData()); + if (e.getData().toLowerCase().contains("no keys found")) { + return results; + } else if (e.getData().toLowerCase().contains("too many")) { + throw new TooManyResponses(); + } else if (e.getData().toLowerCase().contains("insufficient")) { + throw new InsufficientQuery(); + } + } + throw new QueryException("querying server(s) for '" + mHost + "' failed"); + } + Matcher matcher = PUB_KEY_LINE.matcher(data); while (matcher.find()) { KeyInfo info = new KeyInfo(); @@ -94,21 +177,15 @@ public class HkpKeyServer extends KeyServer { @Override String get(long keyId) - throws MalformedURLException, IOException { - String url = "http://" + mHost + ":" + mPort + - "/pks/lookup?op=get&search=0x" + Apg.keyToHex(keyId); - URL realUrl = new URL(url); - URLConnection conn = realUrl.openConnection(); - InputStream is = conn.getInputStream(); - ByteArrayOutputStream raw = new ByteArrayOutputStream(); + throws QueryException { + String request = "/pks/lookup?op=get&search=0x" + Apg.keyToHex(keyId); - byte buffer[] = new byte[1 << 16]; - int n = 0; - while ((n = is.read(buffer)) != -1) { - raw.write(buffer, 0, n); + String data = null; + try { + data = query(request); + } catch (HttpError e) { + throw new QueryException("not found"); } - - String data = raw.toString(); Matcher matcher = Apg.PGP_PUBLIC_KEY.matcher(data); if (matcher.find()) { return matcher.group(1); diff --git a/src/org/thialfihar/android/apg/KeyServer.java b/src/org/thialfihar/android/apg/KeyServer.java index 25ff26144..b31ace887 100644 --- a/src/org/thialfihar/android/apg/KeyServer.java +++ b/src/org/thialfihar/android/apg/KeyServer.java @@ -1,13 +1,23 @@ package org.thialfihar.android.apg; -import java.io.IOException; import java.io.Serializable; -import java.net.MalformedURLException; import java.util.Date; import java.util.List; import java.util.Vector; public abstract class KeyServer { + static public class QueryException extends Exception { + private static final long serialVersionUID = 2703768928624654512L; + public QueryException(String message) { + super(message); + } + } + static public class TooManyResponses extends Exception { + private static final long serialVersionUID = 2703768928624654513L; + } + static public class InsufficientQuery extends Exception { + private static final long serialVersionUID = 2703768928624654514L; + } static public class KeyInfo implements Serializable { private static final long serialVersionUID = -7797972113284992662L; Vector userIds; @@ -18,6 +28,6 @@ public abstract class KeyServer { int size; String algorithm; } - abstract List search(String query) throws MalformedURLException, IOException; - abstract String get(long keyId) throws MalformedURLException, IOException; + abstract List search(String query) throws QueryException, TooManyResponses, InsufficientQuery; + abstract String get(long keyId) throws QueryException; } diff --git a/src/org/thialfihar/android/apg/KeyServerQueryActivity.java b/src/org/thialfihar/android/apg/KeyServerQueryActivity.java index e7f393756..1d3a476e1 100644 --- a/src/org/thialfihar/android/apg/KeyServerQueryActivity.java +++ b/src/org/thialfihar/android/apg/KeyServerQueryActivity.java @@ -1,11 +1,12 @@ package org.thialfihar.android.apg; -import java.io.IOException; -import java.net.MalformedURLException; import java.util.List; import java.util.Vector; +import org.thialfihar.android.apg.KeyServer.InsufficientQuery; import org.thialfihar.android.apg.KeyServer.KeyInfo; +import org.thialfihar.android.apg.KeyServer.QueryException; +import org.thialfihar.android.apg.KeyServer.TooManyResponses; import android.app.Activity; import android.content.Context; @@ -65,14 +66,13 @@ public class KeyServerQueryActivity extends BaseActivity { search(query); } }); - - mQuery.setText("cartman"); } private void search(String query) { showDialog(Id.dialog.querying); mQueryType = Id.query.search; mQueryString = query; + mAdapter.setKeys(new Vector()); startThread(); } @@ -90,16 +90,18 @@ public class KeyServerQueryActivity extends BaseActivity { Message msg = new Message(); try { - HkpKeyServer server = new HkpKeyServer("198.128.3.63");//"pool.sks-keyservers.net"); + HkpKeyServer server = new HkpKeyServer("pool.sks-keyservers.net"); if (mQueryType == Id.query.search) { mSearchResult = server.search(mQueryString); } else if (mQueryType == Id.query.get) { mKeyData = server.get(mQueryId); } - } catch (MalformedURLException e) { - error = "" + e; - } catch (IOException e) { + } catch (QueryException e) { error = "" + e; + } catch (InsufficientQuery e) { + error = "Insufficient query."; + } catch (TooManyResponses e) { + error = "Too many responses."; } data.putInt(Apg.EXTRA_STATUS, Id.message.done); @@ -127,6 +129,7 @@ public class KeyServerQueryActivity extends BaseActivity { if (mQueryType == Id.query.search) { if (mSearchResult != null) { + Toast.makeText(this, getString(R.string.keysFound, mSearchResult.size()), Toast.LENGTH_SHORT).show(); mAdapter.setKeys(mSearchResult); } } else if (mQueryType == Id.query.get) {