/** @param requiresAuthorization */ protected static void processQueue(String queueID, Map mapQueue) { if (!initialized) { init(); } final Map mapProcessing = new HashMap(); boolean sendAZID = true; long contentNetworkID = ContentNetwork.CONTENT_NETWORK_VUZE; // Create urlStem (or post data) boolean isMulti = false; StringBuffer urlStem = new StringBuffer(); long sequenceNo = 0; Map<String, Object> mapPayload = new HashMap<String, Object>(); mapPayload.put("azid", ConstantsVuze.AZID); mapPayload.put("azv", Constants.AZUREUS_VERSION); // mapPayload.put("mode", FeatureManagerUI.getMode()); mapPayload.putAll(mapExtra); List<Map> listCommands = new ArrayList<Map>(); mapPayload.put("commands", listCommands); queue_mon.enter(); try { String lastServer = null; // add one at a time, ensure relay server messages are seperate boolean first = true; for (Iterator iter = mapQueue.keySet().iterator(); iter.hasNext(); ) { PlatformMessage message = (PlatformMessage) iter.next(); Object value = mapQueue.get(message); Map<String, Object> mapCmd = new HashMap<String, Object>(); if (first) { sendAZID = message.sendAZID(); contentNetworkID = message.getContentNetworkID(); first = false; } // build urlStem message.setSequenceNo(sequenceNo); if (urlStem.length() > 0) { urlStem.append('&'); } String listenerID = message.getListenerID(); String messageID = message.getMessageID(); Map params = message.getParameters(); try { urlStem.append("msg="); urlStem.append(URLEncoder.encode(listenerID, "UTF-8")); urlStem.append(":"); urlStem.append(URLEncoder.encode(message.getOperationID(), "UTF-8")); } catch (UnsupportedEncodingException e) { } mapCmd.put("seq-id", sequenceNo); mapCmd.put("listener-id", listenerID); mapCmd.put("op-id", message.getOperationID()); if (params != null) { mapCmd.put("values", params); } listCommands.add(mapCmd); // We used to check on MAX_POST_LENGTH, but with the changes that // would require converting the map to JSON on every iteration to get // the length. For now, just limit to 10 if (sequenceNo > 10) { debug("breaking up batch at " + sequenceNo + " because max limit would be exceeded"); break; } String curServer = messageID + "-" + listenerID; if (lastServer != null && !lastServer.equals(curServer)) { isMulti = true; } lastServer = curServer; PlatformMessengerListener listener = (PlatformMessengerListener) mapProcessing.get(message); if (listener != null) { listener.messageSent(message); } sequenceNo++; // Adjust lists mapProcessing.put(message, value); iter.remove(); if (!getAllowMulti()) { break; } } } finally { queue_mon.exit(); } // debug("about to process " + mapProcessing.size()); if (mapProcessing.size() == 0) { return; } // Build base RPC url based on listener and server // one day all this URL hacking should be moved into the ContentNetwork... ContentNetwork cn = ContentNetworkManagerFactory.getSingleton().getContentNetwork(contentNetworkID); if (cn == null) { cn = ConstantsVuze.getDefaultContentNetwork(); } String sURL_RPC = ContentNetworkUtils.getUrl(cn, ContentNetwork.SERVICE_RPC) + "&" + urlStem.toString(); // Build full url and data to send String sURL; String sPostData = null; String sJSONPayload = UrlUtils.encode(JSONUtils.encodeToJSON(mapPayload)); if (USE_HTTP_POST) { sURL = sURL_RPC; sPostData = URL_POST_PLATFORM_DATA + "&payload=" + sJSONPayload; sPostData = cn.appendURLSuffix(sPostData, true, sendAZID); if (DEBUG_URL) { debug("POST for " + mapProcessing.size() + ": " + sURL + "\n DATA: " + sPostData); } else { debug("POST for " + mapProcessing.size() + ": " + sURL); } } else { sURL = sURL_RPC + URL_PLATFORM_MESSAGE + "&payload=" + sJSONPayload; sURL = cn.appendURLSuffix(sURL, false, sendAZID); if (DEBUG_URL) { debug("GET: " + sURL); } else { debug("GET: " + sURL_RPC + URL_PLATFORM_MESSAGE); } } final String fURL = sURL; final String fPostData = sPostData; // one at a time to take advantage of keep-alive connections dispatcher.dispatch( new AERunnable() { public void runSupport() { try { processQueueAsync(fURL, fPostData, mapProcessing); } catch (Throwable e) { if (e instanceof ResourceDownloaderException) { debug("Error while sending message(s) to Platform: " + e.toString()); } else { debug("Error while sending message(s) to Platform", e); } for (Iterator iter = mapProcessing.keySet().iterator(); iter.hasNext(); ) { PlatformMessage message = (PlatformMessage) iter.next(); PlatformMessengerListener l = (PlatformMessengerListener) mapProcessing.get(message); if (l != null) { try { HashMap map = new HashMap(); map.put("text", e.toString()); map.put("Throwable", e); l.replyReceived(message, REPLY_EXCEPTION, map); } catch (Throwable e2) { debug("Error while sending replyReceived", e2); } } } } } }); }
/** * @param mapProcessing * @param surl * @throws Exception */ protected static void processQueueAsync(String sURL, String sData, Map mapProcessing) throws Exception { URL url; url = new URL(sURL); String s; byte[] bytes = downloadURL(url, sData); s = new String(bytes, "UTF8"); Map mapAllReplies = JSONUtils.decodeJSON(s); List listReplies = MapUtils.getMapList(mapAllReplies, "replies", null); if (mapAllReplies == null || listReplies == null || listReplies.isEmpty()) { debug( "Error while sending message(s) to Platform: reply: " + s + "\nurl: " + sURL + "\nPostData: " + sData); for (Iterator iter = mapProcessing.keySet().iterator(); iter.hasNext(); ) { PlatformMessage message = (PlatformMessage) iter.next(); PlatformMessengerListener l = (PlatformMessengerListener) mapProcessing.get(message); if (l != null) { try { HashMap map = new HashMap(); map.put("text", "result was " + s); l.replyReceived(message, REPLY_EXCEPTION, map); } catch (Throwable e2) { debug( "Error while sending replyReceived" + "\nurl: " + sURL + "\nPostData: " + sData, e2); } } } return; } Map<Long, Map> mapOrder = new HashMap<Long, Map>(); for (Object reply : listReplies) { if (reply instanceof Map) { mapOrder.put(MapUtils.getMapLong((Map) reply, "seq-id", -1), (Map) reply); } } for (Iterator iter = mapProcessing.keySet().iterator(); iter.hasNext(); ) { PlatformMessage message = (PlatformMessage) iter.next(); PlatformMessengerListener l = (PlatformMessengerListener) mapProcessing.get(message); if (l == null) { continue; } Map mapReply = mapOrder.get(new Long(message.getSequenceNo())); if (mapReply == null) { debug("No reply for " + message.toShortString()); } String replyType = MapUtils.getMapString(mapReply, "type", "payload"); Map payload; if (replyType.equalsIgnoreCase("payload")) { payload = MapUtils.getMapMap(mapReply, "payload", Collections.EMPTY_MAP); } else { payload = new HashMap(); payload.put("message", MapUtils.getMapString(mapReply, "message", "?")); } if (mapReply != null) { String reply = JSONUtils.encodeToJSON(payload); debug( "Got a reply for " + message.toShortString() + "\n\t" + reply.substring(0, Math.min(8192, reply.length()))); } try { l.replyReceived(message, replyType, payload); } catch (Exception e2) { debug("Error while sending replyReceived", e2); } } }
protected boolean handleLocalTunnel( TrackerWebPageRequest request, TrackerWebPageResponse response) throws IOException { start(); if (SRP_VERIFIER == null || !active) { throw (new IOException("Secure pairing is not enabled")); } boolean good_request = false; try { // remove /pairing/tunnel/ String url = request.getURL().substring(16); int q_pos = url.indexOf('?'); Map<String, String> args = new HashMap<String, String>(); if (q_pos != -1) { String args_str = url.substring(q_pos + 1); String[] bits = args_str.split("&"); for (String arg : bits) { String[] x = arg.split("="); if (x.length == 2) { args.put(x[0].toLowerCase(), x[1]); } } url = url.substring(0, q_pos); } if (url.startsWith("create")) { String ac = args.get("ac"); String sid = args.get("sid"); if (ac == null || sid == null) { throw (new IOException("Access code or service id missing")); } if (!ac.equals(manager.peekAccessCode())) { throw (new IOException("Invalid access code")); } PairedServiceImpl ps = manager.getService(sid); if (ps == null) { good_request = true; throw (new IOException("Service '" + sid + "' not registered")); } PairedServiceRequestHandler handler = ps.getHandler(); if (handler == null) { good_request = true; throw (new IOException("Service '" + sid + "' has no handler registered")); } JSONObject json = new JSONObject(); JSONObject result = new JSONObject(); json.put("result", result); byte[] ss = new byte[] {SRP_SALT[0], SRP_SALT[1], SRP_SALT[2], SRP_SALT[3]}; long tunnel_id = RandomUtils.nextSecureAbsoluteLong(); String tunnel_name = Base32.encode(ss) + "_" + tunnel_id; synchronized (local_server_map) { long diff = SystemTime.getMonotonousTime() - last_local_server_create_time; if (diff < 5000) { try { long sleep = 5000 - diff; System.out.println("Sleeping for " + sleep + " before starting srp"); Thread.sleep(sleep); } catch (Throwable e) { } } SRP6Server server = new SRP6Server(); server.init(N_3072, G_3072, SRP_VERIFIER, new SHA256Digest(), RandomUtils.SECURE_RANDOM); BigInteger B = server.generateServerCredentials(); local_server_map.put(tunnel_name, new Object[] {server, handler, null, null}); last_local_server_create_time = SystemTime.getMonotonousTime(); total_local_servers++; result.put("srp_salt", Base32.encode(SRP_SALT)); result.put("srp_b", Base32.encode(B.toByteArray())); Map<String, String> headers = request.getHeaders(); String host = headers.get("host"); // remove port number int pos = host.lastIndexOf("]"); if (pos != -1) { // ipv6 literal host = host.substring(0, pos + 1); } else { pos = host.indexOf(':'); if (pos != -1) { host = host.substring(0, pos); } } String abs_url = request.getAbsoluteURL().toString(); // unfortunately there is some nasty code that uses a configured tracker // address as the default host abs_url = UrlUtils.setHost(new URL(abs_url), host).toExternalForm(); pos = abs_url.indexOf("/create"); String tunnel_url = abs_url.substring(0, pos) + "/id/" + tunnel_name; result.put("url", tunnel_url); } response.getOutputStream().write(JSONUtils.encodeToJSON(json).getBytes("UTF-8")); response.setContentType("application/json; charset=UTF-8"); response.setGZIP(true); good_request = true; return (true); } else if (url.startsWith("id/")) { String tunnel_name = url.substring(3); Object[] entry; synchronized (local_server_map) { entry = local_server_map.get(tunnel_name); if (entry == null) { good_request = true; throw (new IOException("Unknown tunnel id")); } } String srp_a = args.get("srp_a"); String enc_data = args.get("enc_data"); String enc_iv = args.get("enc_iv"); if (srp_a != null && enc_data != null && enc_iv != null) { try { synchronized (local_server_map) { long diff = SystemTime.getMonotonousTime() - last_local_server_agree_time; if (diff < 5000) { try { long sleep = 5000 - diff; System.out.println("Sleeping for " + sleep + " before completing srp"); Thread.sleep(sleep); } catch (Throwable e) { } } } JSONObject json = new JSONObject(); JSONObject result = new JSONObject(); json.put("result", result); SRP6Server server = (SRP6Server) entry[0]; BigInteger A = new BigInteger(Base32.decode(srp_a)); BigInteger serverS = server.calculateSecret(A); byte[] shared_secret = serverS.toByteArray(); Cipher decipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); byte[] key = new byte[16]; System.arraycopy(shared_secret, 0, key, 0, 16); SecretKeySpec secret = new SecretKeySpec(key, "AES"); decipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(Base32.decode(enc_iv))); byte[] dec = decipher.doFinal(Base32.decode(enc_data)); JSONObject dec_json = (JSONObject) JSONUtils.decodeJSON(new String(dec, "UTF-8")); String tunnel_url = (String) dec_json.get("url"); if (!tunnel_url.contains(tunnel_name)) { throw (new IOException("Invalid tunnel url")); } String endpoint_url = (String) dec_json.get("endpoint"); entry[2] = secret; entry[3] = endpoint_url; result.put("state", "activated"); response.getOutputStream().write(JSONUtils.encodeToJSON(json).getBytes("UTF-8")); response.setContentType("application/json; charset=UTF-8"); response.setGZIP(true); good_request = true; return (true); } catch (Throwable e) { throw (new IOException(Debug.getNestedExceptionMessage(e))); } finally { last_local_server_agree_time = SystemTime.getMonotonousTime(); } } else if (args.containsKey("close")) { synchronized (local_server_map) { local_server_map.remove(tunnel_name); } good_request = true; return (true); } else { PairedServiceRequestHandler request_handler = (PairedServiceRequestHandler) entry[1]; SecretKeySpec secret = (SecretKeySpec) entry[2]; String endpoint_url = (String) entry[3]; if (secret == null) { throw (new IOException("auth not completed")); } byte[] request_data = FileUtil.readInputStreamAsByteArray(request.getInputStream()); try { byte[] decrypted; { byte[] IV = new byte[16]; System.arraycopy(request_data, 0, IV, 0, IV.length); Cipher decipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); decipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(IV)); decrypted = decipher.doFinal(request_data, 16, request_data.length - 16); } byte[] reply_bytes = request_handler.handleRequest( request.getClientAddress2().getAddress(), endpoint_url, decrypted); { Cipher encipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); encipher.init(Cipher.ENCRYPT_MODE, secret); AlgorithmParameters params = encipher.getParameters(); byte[] IV = params.getParameterSpec(IvParameterSpec.class).getIV(); byte[] enc = encipher.doFinal(reply_bytes); byte[] rep_bytes = new byte[IV.length + enc.length]; System.arraycopy(IV, 0, rep_bytes, 0, IV.length); System.arraycopy(enc, 0, rep_bytes, IV.length, enc.length); response.getOutputStream().write(rep_bytes); response.setContentType("application/octet-stream"); good_request = true; return (true); } } catch (Throwable e) { throw (new IOException(Debug.getNestedExceptionMessage(e))); } } } throw (new IOException("Unknown tunnel operation")); } finally { if (!good_request) { manager.recordRequest( "SRP", request.getClientAddress2().getAddress().getHostAddress(), false); } } }