package com.subgraph.orchid.directory.consensus; import java.util.Arrays; import java.util.List; import com.subgraph.orchid.ConsensusDocument.ConsensusFlavor; import com.subgraph.orchid.TorParsingException; import com.subgraph.orchid.directory.consensus.ConsensusDocumentParser.DocumentSection; import com.subgraph.orchid.directory.parsing.DocumentFieldParser; import com.subgraph.orchid.directory.parsing.NameIntegerParameter; public class PreambleSectionParser extends ConsensusDocumentSectionParser { private final static int CURRENT_DOCUMENT_VERSION = 3; private boolean isFirstLine = true; PreambleSectionParser(DocumentFieldParser parser, ConsensusDocumentImpl document) { super(parser, document); } String getNextStateKeyword() { return "dir-source"; } DocumentSection getSection() { return DocumentSection.PREAMBLE; } DocumentSection nextSection() { return DocumentSection.AUTHORITY; } @Override void parseLine(DocumentKeyword keyword) { if(isFirstLine) { parseFirstLine(keyword); } else { processKeyword(keyword); } } private void processKeyword(DocumentKeyword keyword) { switch(keyword) { case NETWORK_STATUS_VERSION: throw new TorParsingException("Network status version may only appear on the first line of status document"); case VOTE_STATUS: final String voteStatus = fieldParser.parseString(); if(!voteStatus.equals("consensus")) throw new TorParsingException("Unexpected vote-status type: "+ voteStatus); break; case CONSENSUS_METHOD: document.setConsensusMethod(fieldParser.parseInteger()); break; case VALID_AFTER: document.setValidAfter(fieldParser.parseTimestamp()); break; case FRESH_UNTIL: document.setFreshUntil(fieldParser.parseTimestamp()); break; case VALID_UNTIL: document.setValidUntil(fieldParser.parseTimestamp()); break; case VOTING_DELAY: document.setVoteDelaySeconds(fieldParser.parseInteger()); document.setDistDelaySeconds(fieldParser.parseInteger()); break; case CLIENT_VERSIONS: for(String version: parseVersions(fieldParser.parseString())) document.addClientVersion(version); break; case SERVER_VERSIONS: for(String version: parseVersions(fieldParser.parseString())) document.addServerVersion(version); break; case KNOWN_FLAGS: while(fieldParser.argumentsRemaining() > 0) document.addKnownFlag(fieldParser.parseString()); break; case PARAMS: parseParams(); break; default: break; } } private void parseFirstLine(DocumentKeyword keyword) { if(keyword != DocumentKeyword.NETWORK_STATUS_VERSION) throw new TorParsingException("network-status-version not found at beginning of consensus document as expected."); final int documentVersion = fieldParser.parseInteger(); if(documentVersion != CURRENT_DOCUMENT_VERSION) throw new TorParsingException("Unexpected consensus document version number: " + documentVersion); if(fieldParser.argumentsRemaining() > 0) { parseConsensusFlavor(); } isFirstLine = false; } private void parseConsensusFlavor() { final String flavor = fieldParser.parseString(); if("ns".equals(flavor)) { document.setConsensusFlavor(ConsensusFlavor.NS); } else if("microdesc".equals(flavor)) { document.setConsensusFlavor(ConsensusFlavor.MICRODESC); } else { fieldParser.logWarn("Unknown consensus flavor: "+ flavor); } } private List<String> parseVersions(String versions) { return Arrays.asList(versions.split(",")); } private void parseParams() { final int remaining = fieldParser.argumentsRemaining(); for(int i = 0; i < remaining; i++) { NameIntegerParameter p = fieldParser.parseParameter(); document.addParameter(p.getName(), p.getValue()); } } }