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 28eb6c1259..c4706ffe21 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 0d8b8b62cc..00638a1198 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 8ae1d0001e..404b1ba995 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; + } } ////////////////////////////////////////////////