-
Notifications
You must be signed in to change notification settings - Fork 208
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'patch-1' of github.com:torusrxxx/fred into next
- Loading branch information
Showing
9 changed files
with
620 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
|
||
} |
Oops, something went wrong.