From 6aa81f7ec60b602fbc1791de5fa0faf62fb3aabd Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 22 May 2020 18:22:56 +0000 Subject: [PATCH] UPnP fixes part 6: Fix malformed HTTP requests Check HTTP response code in Parser Check content type in Parser Debug log in Parser Show device URL in CLI Don't retry after parser exception Close resources in finally block --- .../src/net/i2p/router/transport/UPnP.java | 3 ++ .../org/cybergarage/upnp/ControlPoint.java | 16 +++++-- .../java/src/org/cybergarage/xml/Parser.java | 47 +++++++++++++++---- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/UPnP.java b/router/java/src/net/i2p/router/transport/UPnP.java index 28eb6c125..c4706ffe2 100644 --- a/router/java/src/net/i2p/router/transport/UPnP.java +++ b/router/java/src/net/i2p/router/transport/UPnP.java @@ -1634,6 +1634,9 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis ": " + DataHelper.escapeHTML(device.getFriendlyName()) + ""); System.out.println("

UDN: " + DataHelper.escapeHTML(device.getUDN())); System.out.println("
IP: " + getIP(device)); + String loc = device.getLocation(); + if (loc != null && loc.length() > 0) + System.out.println("
URL: " + loc + ""); System.out.println(sb.toString()); sb.setLength(0); } diff --git a/router/java/src/org/cybergarage/upnp/ControlPoint.java b/router/java/src/org/cybergarage/upnp/ControlPoint.java index 0d8b8b62c..00638a119 100644 --- a/router/java/src/org/cybergarage/upnp/ControlPoint.java +++ b/router/java/src/org/cybergarage/upnp/ControlPoint.java @@ -64,6 +64,7 @@ package org.cybergarage.upnp; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; +import java.util.Locale; import org.cybergarage.http.HTTPRequest; import org.cybergarage.http.HTTPRequestListener; @@ -253,6 +254,15 @@ public class ControlPoint implements HTTPRequestListener String location = ssdpPacket.getLocation(); try { URL locationUrl = new URL(location); + // I2P + // Roku fake json port, the real UPnP port is 8060 + if (locationUrl.getPort() == 9080) { + String lcusn = usn.toLowerCase(Locale.US); + if (lcusn.contains("rku") || lcusn.contains("roku")) { + Debug.warning("Ignoring Roku at " + location); + return; + } + } Parser parser = UPnP.getXMLParser(); Node rootNode = parser.parse(locationUrl); Device rootDev = getDevice(rootNode); @@ -270,12 +280,10 @@ public class ControlPoint implements HTTPRequestListener performAddDeviceListener( rootDev ); } catch (MalformedURLException me) { - Debug.warning(ssdpPacket.toString()); - Debug.warning(me); + Debug.warning("Bad location: " + location, me); } catch (ParserException pe) { - Debug.warning(ssdpPacket.toString()); - Debug.warning(pe); + Debug.warning("Error parsing data at location: " + location, pe); } } diff --git a/router/java/src/org/cybergarage/xml/Parser.java b/router/java/src/org/cybergarage/xml/Parser.java index 8ae1d0001..404b1ba99 100644 --- a/router/java/src/org/cybergarage/xml/Parser.java +++ b/router/java/src/org/cybergarage/xml/Parser.java @@ -21,6 +21,7 @@ package org.cybergarage.xml; import java.io.File; import java.io.FileInputStream; +import java.io.IOException; import java.io.InputStream; import java.io.ByteArrayInputStream; import java.net.HttpURLConnection; @@ -29,6 +30,7 @@ import java.net.URL; import org.cybergarage.http.HTTP; import org.cybergarage.http.HTTPRequest; import org.cybergarage.http.HTTPResponse; +import org.cybergarage.util.Debug; public abstract class Parser { @@ -58,9 +60,15 @@ public abstract class Parser if (port == -1) port = 80; String uri = locationURL.getPath(); + // I2P note: Roku port 9080 now ignored in ControlPoint.addDevice() + // I2P fix - Roku + if (uri.length() <= 0) + uri = "/"; + HttpURLConnection urlCon = null; + InputStream urlIn = null; try { - HttpURLConnection urlCon = (HttpURLConnection)locationURL.openConnection(); + urlCon = (HttpURLConnection)locationURL.openConnection(); // I2P mods to prevent hangs (see HTTPRequest for more info) // this seems to work, getInputStream actually does the connect(), // (as shown by a thread dump) @@ -73,29 +81,50 @@ public abstract class Parser if (host != null) urlCon.setRequestProperty(HTTP.HOST, host); - InputStream urlIn = urlCon.getInputStream(); + // I2P fix + int code = urlCon.getResponseCode(); + if (code < 200 || code >= 300) + throw new ParserException("Bad response code " + code); + // I2P fix - Roku port 9080 + // not valid json either; returns "status=ok" + if ("application/json".equals(urlCon.getContentType())) + throw new ParserException("JSON response"); + urlIn = urlCon.getInputStream(); Node rootElem = parse(urlIn); - - urlIn.close(); - urlCon.disconnect(); return rootElem; + } catch (ParserException pe) { + throw pe; } catch (Exception e) { + // Why try twice??? //throw new ParserException(e); + Debug.warning("Failed fetch but retrying with HTTPRequest, URL: " + locationURL, e); + } finally { + if (urlIn != null) try { urlIn.close(); } catch (IOException ioe) {} + if (urlCon != null) urlCon.disconnect(); } HTTPRequest httpReq = new HTTPRequest(); httpReq.setMethod(HTTP.GET); httpReq.setURI(uri); HTTPResponse httpRes = httpReq.post(host, port); - if (httpRes.isSuccessful() == false) - throw new ParserException("HTTP comunication failed: no answer from peer." + - "Unable to retrive resoure -> "+locationURL.toString()); + if (!httpRes.isSuccessful()) + throw new ParserException("HTTP comunication failed. " + + "Unable to retrieve resource -> " + locationURL + + "\nRequest:\n" + httpReq + + "\nResponse:\n" + httpRes); String content = new String(httpRes.getContent()); ByteArrayInputStream strBuf = new ByteArrayInputStream(content.getBytes()); - return parse(strBuf); + try { + return parse(strBuf); + } catch (ParserException pe) { + Debug.warning("Parse error at resource " + locationURL + + "\nRequest:\n" + httpReq + + "\nResponse:\n" + httpRes, pe); + throw pe; + } } ////////////////////////////////////////////////