diff --git a/core/src/main/groovy/com/muwire/core/connection/ConnectionAcceptor.groovy b/core/src/main/groovy/com/muwire/core/connection/ConnectionAcceptor.groovy index 0cfb5e4c..9609fcff 100644 --- a/core/src/main/groovy/com/muwire/core/connection/ConnectionAcceptor.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/ConnectionAcceptor.groovy @@ -188,6 +188,8 @@ class ConnectionAcceptor { throw new IOException("invalid request header") Persona sender = new Persona(dis) + if (sender.destination != e.getDestination()) + throw new IOException("Sender destination mismatch expected $e.getDestination(), got $sender.destination") int nResults = dis.readUnsignedShort() for (int i = 0; i < nResults; i++) { int jsonSize = dis.readUnsignedShort() diff --git a/core/src/main/groovy/com/muwire/core/search/ResultsParser.groovy b/core/src/main/groovy/com/muwire/core/search/ResultsParser.groovy index e90d9010..813e499a 100644 --- a/core/src/main/groovy/com/muwire/core/search/ResultsParser.groovy +++ b/core/src/main/groovy/com/muwire/core/search/ResultsParser.groovy @@ -1,9 +1,54 @@ package com.muwire.core.search +import javax.naming.directory.InvalidSearchControlsException + +import com.muwire.core.InfoHash import com.muwire.core.Persona +import com.muwire.core.util.DataUtil + +import net.i2p.data.Base64 class ResultsParser { public static UIResultEvent parse(Persona p, def json) throws InvalidSearchResultException { - null + if (json.type != "Result") + throw new InvalidSearchResultException("not a result json") + if (json.version != 1) + throw new InvalidSearchResultException("unknown version $json.version") + if (json.name == null) + throw new InvalidSearchResultException("name missing") + if (json.size == null) + throw new InvalidSearchResultException("length missing") + if (json.infohash == null) + throw new InvalidSearchResultException("infohash missing") + if (json.pieceSize == null) + throw new InvalidSearchResultException("pieceSize missing") + if (!(json.hashList instanceof List)) + throw new InvalidSearchResultException("hashlist not a list") + try { + String name = DataUtil.readi18nString(Base64.decode(json.name)) + long size = Long.parseLong(json.size) + byte [] infoHash = Base64.decode(json.infohash) + if (infoHash.length != InfoHash.SIZE) + throw new InvalidSearchResultException("invalid infohash size $infoHash.length") + int pieceSize = Integer.parseInt(json.pieceSize) + byte [] hashList = new byte[json.hashList.size() * InfoHash.SIZE] + json.hashList.eachWithIndex { string, index -> + byte [] hashPiece = Base64.decode(string) + if (hashPiece.length != InfoHash.SIZE) + throw new InvalidSearchResultException("Invalid piece hash size $hashPiece.length at index $index") + System.arraycopy(hashPiece, 0, hashList, index * InfoHash.SIZE, InfoHash.SIZE) + } + InfoHash parsedIH = InfoHash.fromHashList(hashList) + if (parsedIH.getRoot() != infoHash) + throw new InvalidSearchControlsException("infohash root doesn't match") + + return new UIResultEvent( sender : p, + name : name, + size : size, + infohash : parsedIH, + pieceSize : pieceSize) + } catch (Exception e) { + throw new InvalidSearchResultException("parsing search result failed",e) + } } } diff --git a/core/src/main/groovy/com/muwire/core/search/ResultsSender.groovy b/core/src/main/groovy/com/muwire/core/search/ResultsSender.groovy index f58c1198..a1b2b32b 100644 --- a/core/src/main/groovy/com/muwire/core/search/ResultsSender.groovy +++ b/core/src/main/groovy/com/muwire/core/search/ResultsSender.groovy @@ -48,9 +48,16 @@ class ResultsSender { void sendResults(UUID uuid, SharedFile[] results, Destination target) { log.info("Sending $results.length results for uuid $uuid to ${target.toBase32()}") if (target.equals(me.destination)) { - def resultEvent = new ResultsEvent( uuid : uuid, results : results ) - def uiResultEvent = new UIResultEvent(sender: me, resultsEvent : resultEvent) - eventBus.publish(uiResultEvent) + results.each { + long length = it.getFile().length() + def uiResultEvent = new UIResultEvent( sender : me, + name : it.getFile().getName(), + size : length, + infohash : it.getInfoHash(), + pieceSize : FileHasher.getPieceSize(length) + ) + eventBus.publish(uiResultEvent) + } } else { executor.execute(new ResultSendJob(uuid : uuid, results : results, target: target)) } diff --git a/core/src/main/groovy/com/muwire/core/search/UIResultEvent.groovy b/core/src/main/groovy/com/muwire/core/search/UIResultEvent.groovy index be0a1882..1ed1e1dd 100644 --- a/core/src/main/groovy/com/muwire/core/search/UIResultEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/search/UIResultEvent.groovy @@ -1,9 +1,13 @@ package com.muwire.core.search import com.muwire.core.Event +import com.muwire.core.InfoHash import com.muwire.core.Persona class UIResultEvent extends Event { Persona sender - ResultsEvent resultsEvent + String name + long size + InfoHash infohash + int pieceSize } diff --git a/core/src/main/groovy/com/muwire/core/util/DataUtil.groovy b/core/src/main/groovy/com/muwire/core/util/DataUtil.groovy index 2f9b46f4..23d3bdb6 100644 --- a/core/src/main/groovy/com/muwire/core/util/DataUtil.groovy +++ b/core/src/main/groovy/com/muwire/core/util/DataUtil.groovy @@ -1,5 +1,7 @@ package com.muwire.core.util +import java.nio.charset.StandardCharsets + class DataUtil { private final static int MAX_SHORT = (0x1 << 16) - 1 @@ -36,4 +38,15 @@ class DataUtil { (((int)(header[1] & 0xFF) << 8)) | ((int)header[2] & 0xFF) } + + static String readi18nString(byte [] encoded) { + if (encoded.length < 2) + throw new IllegalArgumentException("encoding too short $encoded.length") + int length = ((encoded[0] & 0xFF) << 8) | (encoded[1] & 0xFF) + if (encoded.length != length + 2) + throw new IllegalArgumentException("encoding doesn't match length, expected $length found $encoded.length") + byte [] string = new byte[length] + System.arraycopy(encoded, 2, string, 0, length) + new String(string, StandardCharsets.UTF_8) + } }