Skip to content

Commit

Permalink
Merge branch 'patch-1' of github.com:torusrxxx/fred into next
Browse files Browse the repository at this point in the history
  • Loading branch information
ArneBab committed Sep 22, 2024
2 parents c2a0431 + 523b6f0 commit 6d58bed
Show file tree
Hide file tree
Showing 9 changed files with 620 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/freenet/client/DefaultMIMETypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,7 @@ public synchronized static short byName(String s) {
addMIMEType((short)619, "audio/speex", "spx");
addMIMEType((short)620, "audio/ogg", "oga");
addMIMEType((short)621, "audio/flac", "flac");
addMIMEType((short)622, "image/webp", "webp");
}

/** Guess a MIME type from a filename.
Expand Down
8 changes: 7 additions & 1 deletion src/freenet/client/filter/ContentFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ true, false, new PNGFilter(true, true, true), false, false, false, false, true,
true, false, new BMPFilter(), false, false, false, false, true, false,
l10n("imageBMPReadAdvice"),
false, null, null, false));

// WEBP - has a filter
register(new FilterMIMEType("image/webp", "webp", new String[] { "image/webp" }, new String[0],
true, false, new WebPFilter(), false, false, false, false, true, false,
l10n("imageWebPReadAdvice"),
false, null, null, false));

/* Ogg - has a filter
* Xiph's container format. Contains one or more logical bitstreams.
Expand All @@ -92,7 +98,7 @@ true, false, new BMPFilter(), false, false, false, false, true, false,
true, false, new OggFilter(), true, true, false, true, false, false,
l10n("containerOggReadAdvice"),false, null, null, false));

/* FLAC - Needs filter
/* FLAC - has a filter
* Lossless audio format. This data is sometimes encapsulated inside
* of ogg containers. It is, however, not currently supported, and
* is very dangerous, as it may specify URLs from which album art
Expand Down
192 changes: 192 additions & 0 deletions src/freenet/client/filter/RIFFFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package freenet.client.filter;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.EOFException;
import java.util.Map;

import freenet.l10n.NodeL10n;
import freenet.support.Logger;
import freenet.support.Logger.LogLevel;

/** RIFF file format filter for several formats, such as AVI, WAV, MID, and WebP
*
*/
public abstract class RIFFFilter implements ContentDataFilter {
private static final byte[] magicNumber = new byte[] {'R', 'I', 'F', 'F'};

@Override
public void readFilter(InputStream input, OutputStream output, String charset, Map<String, String> otherParams,
String schemeHostAndPort, FilterCallback cb) throws DataFilterException, IOException {
DataInputStream in = new DataInputStream(input);
DataOutputStream out = new DataOutputStream(output);
for(byte magicCharacter : magicNumber) {
if(magicCharacter != in.readByte()) throw new DataFilterException(l10n("invalidTitle"), l10n("invalidTitle"), l10n("invalidStream"));
}
int fileSize = readLittleEndianInt(in);
for(byte magicCharacter : getChunkMagicNumber()) {
if(magicCharacter != in.readByte()) throw new DataFilterException(l10n("invalidTitle"), l10n("invalidTitle"), l10n("invalidStream"));
}
out.write(magicNumber);
if(fileSize < 0) {
// FIXME Video with more than 2 GiB data need unsigned format
throw new DataFilterException(l10n("invalidTitle"), l10n("invalidTitle"), l10n("data2GB"));
}
if(fileSize < 12) {
// There couldn't be any chunk in such a small file
throw new DataFilterException(l10n("invalidTitle"), l10n("invalidTitle"), NodeL10n.getBase().getString("ContentFilter.EOFMessage"));
}
writeLittleEndianInt(out, fileSize);
out.write(getChunkMagicNumber());

Object context = createContext();
byte[] fccType;
int ckSize;
int remainingSize = fileSize - 4;
try {
do {
fccType = new byte[4];
in.readFully(fccType);
ckSize = readLittleEndianInt(in);
if(ckSize < 0 || remainingSize < ckSize + 8 + (ckSize & 1)) {
throw new DataFilterException(l10n("invalidTitle"), l10n("invalidTitle"), l10n("dataTooBig"));
}
remainingSize -= ckSize + 8 + (ckSize & 1);
readFilterChunk(fccType, ckSize, context, in, out, charset, otherParams, schemeHostAndPort, cb);
} while(remainingSize != 0);
} catch(EOFException e) {
throw new DataFilterException(l10n("invalidTitle"), l10n("invalidTitle"), NodeL10n.getBase().getString("ContentFilter.EOFMessage"));
}
// Testing if there is any unprocessed bytes left
if(input.read() != -1) {
// A byte is after expected EOF
throw new DataFilterException(l10n("invalidTitle"), l10n("invalidTitle"), NodeL10n.getBase().getString("ContentFilter.EOFMessage"));
}
// Do a final test
if(remainingSize != 0) {
throw new DataFilterException(l10n("invalidTitle"), l10n("invalidTitle"), NodeL10n.getBase().getString("ContentFilter.EOFMessage"));
}
EOFCheck(context);
}

/** Get the FourCC to identify this file format
* @return array of four bytes
*/
protected abstract byte[] getChunkMagicNumber();

/** Create a context object holding the context states
* @return context object
*/
protected abstract Object createContext();

protected abstract void readFilterChunk(byte[] ID, int size, Object context, DataInputStream input, DataOutputStream output, String charset, Map<String, String> otherParams,
String schemeHostAndPort, FilterCallback cb) throws DataFilterException, IOException;

/** Check for invalid conditions after EOF is reached
* @param context context object
* @throws DataFilterException
*/
protected abstract void EOFCheck(Object context) throws DataFilterException;

private static String l10n(String key) {
return NodeL10n.getBase().getString("RIFFFilter."+key);
}

/** Pass through bytes to output unchanged
* @param in Input stream
* @param out Output stream
* @param size Number of bytes to copy
* @throws DataFilterException
* @throws IOException
*/
protected void passthroughBytes(DataInputStream in, DataOutputStream out, int size) throws DataFilterException, IOException {
if(size < 0)
{
if(Logger.shouldLog(LogLevel.WARNING, this.getClass())) Logger.warning(this, "RIFF block size " + size + " is less than 0");
throw new DataFilterException(l10n("invalidTitle"), l10n("invalidTitle"), l10n("dataTooBig"));
} else {
// Copy 1MB at a time instead of all at once
int section;
int remaining = size;
if(remaining > 1024 * 1024) {
section = 1024 * 1024;
} else {
section = remaining;
}
byte[] buf = new byte[section];
while(remaining > 0) {
if(remaining > 1024 * 1024) {
section = 1024 * 1024;
} else {
section = remaining;
}
in.readFully(buf, 0, section);
out.write(buf, 0, section);
remaining -= section;
}
}
}

/** Write a JUNK chunk for unsupported data
* @param in Input stream
* @param out Output stream
* @param size Size of the chunk, if the size is odd, a padding is added
* @throws DataFilterException
* @throws IOException
*/
protected void writeJunkChunk(DataInputStream in, DataOutputStream out, int size) throws DataFilterException, IOException {
size += size % 2; // Add a padding if necessary
if(in.skip(size) < size) {
// EOFException?
throw new EOFException();
}
if(size < 0)
{
if(Logger.shouldLog(LogLevel.WARNING, this.getClass())) Logger.warning(this, "RIFF block size " + size + " is less than 0");
throw new DataFilterException(l10n("invalidTitle"), l10n("invalidTitle"), l10n("dataTooBig"));
} else {
// Write 1MB at a time instead of all at once
int section;
int remaining = size;
byte[] zeros = new byte[1024 * 1024];
for(int i = 0; i < 1024 * 1024; i++) {
zeros[i] = 0;
}
byte[] JUNK = new byte[] {'J', 'U', 'N', 'K'};
out.write(JUNK);
writeLittleEndianInt(out, size);
while(remaining > 0) {
if(remaining > 1024 * 1024) {
section = 1024 * 1024;
} else {
section = remaining;
}
out.write(zeros, 0, section);
remaining -= section;
}
}
}

/** Read a little endian int. readInt and writeInt are big endian, but RIFF use little endian
* @param stream Stream to read from
* @return
* @throws IOException
*/
protected final static int readLittleEndianInt(DataInputStream stream) throws IOException {
int a;
a = stream.readInt();
return Integer.reverseBytes(a);
}

/** Write a little endian int
* @param stream Stream to write to
* @param a
* @throws IOException
*/
protected final static void writeLittleEndianInt(DataOutputStream stream, int a) throws IOException {
stream.writeInt(Integer.reverseBytes(a));
}
}
46 changes: 46 additions & 0 deletions src/freenet/client/filter/VP8PacketFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package freenet.client.filter;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;

public class VP8PacketFilter {
private boolean isWebP;
public VP8PacketFilter(boolean isWebp) {
this.isWebP = isWebp;
}

public void parse(byte[] buf, int size) throws IOException {
try (DataInputStream input = new DataInputStream(new ByteArrayInputStream(buf))) {
// Reference: RFC 6386
// Following code is based on vp8_parse_frame_header from RFC 6386
int[] header = new int[6];
for(int i = 0; i < 6; i++)
header[i] = input.readUnsignedByte();
int sizeInHeader;
boolean isKeyframe;
int tmp = header[0] | (header[1] << 8) | (header[2] << 16);
isKeyframe = (tmp & 1) == 0;
if(!isKeyframe && isWebP) {
throw new DataFilterException("VP8 decode error", "VP8 decode error", "Not a keyframe in WebP image");
}
if((tmp & 0x8) != 0) { //is_experimental bit is unsupported
throw new DataFilterException("VP8 decode error", "VP8 decode error", "VP8 frame version is unsupported");
}
if((tmp & 0x10) == 0 && isWebP) { //is_shown must be true for a WebP image
throw new DataFilterException("VP8 decode error", "VP8 decode error", "WebP frame contains an image without is_shown flag");
}
sizeInHeader = (tmp >> 5) & 0x7ffff;
if(size <= sizeInHeader + (isKeyframe ? 10 : 3)) {
throw new DataFilterException("VP8 decode error", "VP8 decode error", "VP8 frame size is invalid");
}
if(isKeyframe) {
if(header[3] != 0x9d || header[4] != 0x01 || header[5] != 0x2a) {
throw new DataFilterException("VP8 decode error", "VP8 decode error", "VP8 frame sync code is invalid");
}
}
}
// Rest of video: I don't know there is an attack
}

}
Loading

0 comments on commit 6d58bed

Please sign in to comment.