Changeset 75dd2251


Ignore:
Timestamp:
Apr 22, 2016 11:37:55 PM (4 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
e016c87
Parents:
55de82b
Message:

Addressbook: Fix changedest action

  • Implement adddest action
  • Logging improvements

BFNS: Fix lookupAll() NPE

  • Fix addDestination() UOE
  • Support long property values

DataHelper?: Properties methods cleanup
HostTxtEntry?: Test improvements

Files:
4 edited

Legend:

Unmodified
Added
Removed
  • apps/addressbook/java/src/net/i2p/addressbook/Daemon.java

    r55de82b r75dd2251  
    203203                                    if (polddest != null) {
    204204                                        Destination pod = new Destination(polddest);
    205                                         // fill in oldDest for .txt naming service
    206                                         if (isKnown && isTextFile)
    207                                             oldDest = router.lookup(key);
    208                                         if (pod.equals(dest)) {
    209                                             // invalid
    210                                             if (log != null)
    211                                                 log.append("Action: " + action + " failed because" +
    212                                                            " identical old and new destinations for " + key +
    213                                                            " from " + addressbook.getLocation());
    214                                             invalid++;
    215                                             continue;
    216                                         } else if (!isKnown) {
     205                                        List<Destination> pod2 = router.lookupAll(key);
     206                                        if (pod2 == null) {
    217207                                            // we didn't know it before, so we'll add it
    218                                         } else if (dest.equals(oldDest)) {
     208                                            // TODO check inner sig anyway?
     209                                        } else if (pod2.contains(dest)) {
    219210                                            // we knew it before, with the same dest
    220211                                            old++;
    221212                                            continue;
    222                                         } else if (pod.equals(oldDest)) {
     213                                        } else if (pod2.contains(pod)) {
    223214                                            // checks out, so verify the inner sig
    224215                                            if (!he.hasValidInnerSig()) {
     
    227218                                                               " inner signature for key " + key +
    228219                                                               " failed" +
    229                                                                " from " + addressbook.getLocation());
     220                                                               ". From: " + addressbook.getLocation());
    230221                                                invalid++;
    231222                                                continue;
     
    233224                                            // TODO Requires NamingService support
    234225                                            // if (isTextFile), do we replace or not? check sigType.isAvailable()
    235                                             // router.addAltDest(dest)
    236                                             if (log != null)
    237                                                 log.append("Action: " + action + " unimplemented" +
    238                                                            " from " + addressbook.getLocation());
     226                                            boolean success = router.addDestination(key, dest, props);
     227                                            if (log != null) {
     228                                                if (success)
     229                                                    log.append("Additional address for " + key +
     230                                                               " added to address book. From: " + addressbook.getLocation());
     231                                                else
     232                                                    log.append("Failed to add additional address for " + key +
     233                                                               " From: " + addressbook.getLocation());
     234                                            }
     235                                            // now update the published addressbook
     236                                            // ditto
     237                                            if (published != null) {
     238                                                if (publishedNS == null)
     239                                                    publishedNS = new SingleFileNamingService(I2PAppContext.getGlobalContext(), published.getAbsolutePath());
     240                                                success = publishedNS.addDestination(key, dest, props);
     241                                                if (log != null && !success)
     242                                                    log.append("Add to published address book " + published.getAbsolutePath() + " failed for " + key);
     243                                            }
     244                                            nnew++;
     245                                            continue;
     246                                        } else {
     247                                            // mismatch, disallow
     248                                            logMismatch(log, action, key, pod2, he.getDest(), addressbook);
    239249                                            invalid++;
    240250                                            continue;
     
    262272                                        } else {
    263273                                            // mismatch, disallow
    264                                             if (log != null)
    265                                                 log.append("Action: " + action + " failed because" +
    266                                                            " destination for old name " + poldname +
    267                                                            " does not match" +
    268                                                            " from " + addressbook.getLocation());
     274                                            logMismatch(log, action, key, pod, he.getDest(), addressbook);
    269275                                            invalid++;
    270276                                            continue;
     
    273279                                        if (log != null)
    274280                                            log.append("Action: " + action + " failed, missing required parameters" +
    275                                                        " from " + addressbook.getLocation());
     281                                                       ". From: " + addressbook.getLocation());
    276282                                        invalid++;
    277283                                        continue;
     
    293299                                                           " old name " + poldname +
    294300                                                           " is invalid" +
    295                                                            " from " + addressbook.getLocation());
     301                                                           ". From: " + addressbook.getLocation());
    296302                                            invalid++;
    297303                                            continue;
     
    301307                                        if (pod2 == null) {
    302308                                            // we didn't have the old name
     309                                            // TODO check inner sig anyway?
    303310                                        } else if (pod2.contains(pod)) {
    304311                                            // checks out, so verify the inner sig
     
    308315                                                               " inner signature for old name " + poldname +
    309316                                                               " failed" +
    310                                                                " from " + addressbook.getLocation());
     317                                                               ". From: " + addressbook.getLocation());
    311318                                                invalid++;
    312319                                                continue;
     
    314321                                        } else {
    315322                                            // mismatch, disallow
    316                                             if (log != null)
    317                                                 log.append("Action: " + action + " failed because" +
    318                                                            " destination for old name " + poldname +
    319                                                            " does not match provided" +
    320                                                            " from " + addressbook.getLocation());
     323                                            logMismatch(log, action, key, pod2, polddest, addressbook);
    321324                                            invalid++;
    322325                                            continue;
     
    325328                                        if (log != null)
    326329                                            log.append("Action: " + action + " failed, missing required parameters" +
    327                                                        " from " + addressbook.getLocation());
     330                                                       ". From: " + addressbook.getLocation());
    328331                                        invalid++;
    329332                                        continue;
     
    336339                                    if (polddest != null) {
    337340                                        Destination pod = new Destination(polddest);
    338                                         // fill in oldDest for .txt naming service
    339                                         if (isKnown && isTextFile)
    340                                             oldDest = router.lookup(key);
    341                                         if (!isKnown) {
     341                                        List<Destination> pod2 = router.lookupAll(key);
     342                                        if (pod2 == null) {
    342343                                            // we didn't have the old name
    343                                         } else if (pod.equals(oldDest)) {
     344                                            // TODO check inner sig anyway?
     345                                        } else if (pod2.contains(dest)) {
     346                                            // we already have the new dest
     347                                            old++;
     348                                            continue;
     349                                        } else if (pod2.contains(pod)) {
    344350                                            // checks out, so verify the inner sig
    345351                                            if (!he.hasValidInnerSig()) {
     
    348354                                                               " inner signature for key " + key +
    349355                                                               " failed" +
    350                                                                " from " + addressbook.getLocation());
     356                                                               ". From: " + addressbook.getLocation());
    351357                                                invalid++;
    352358                                                continue;
     359                                            }
     360                                            if (log != null) {
     361                                                if (pod2.size() == 1)
     362                                                    log.append("Changing destination for " + key +
     363                                                               ". From: " + addressbook.getLocation());
     364                                                else
     365                                                    log.append("Replacing " + pod2.size() + " destinations for " + key +
     366                                                               ". From: " + addressbook.getLocation());
    353367                                            }
    354368                                            // TODO set flag to do non-putifabsent for published below
    355369                                        } else {
    356370                                            // mismatch, disallow
    357                                             if (log != null)
    358                                                 log.append("Action: " + action + " failed because" +
    359                                                            " destination for key " + key +
    360                                                            " does not match provided" +
    361                                                            " from " + addressbook.getLocation());
     371                                            logMismatch(log, action, key, pod2, polddest, addressbook);
    362372                                            invalid++;
    363373                                            continue;
     
    366376                                        if (log != null)
    367377                                            log.append("Action: " + action + " failed, missing required parameters" +
    368                                                        " from " + addressbook.getLocation());
     378                                                       ". From: " + addressbook.getLocation());
    369379                                        invalid++;
    370380                                        continue;
     
    394404                                                    log.append("Removed: " + poldname +
    395405                                                               " to be replaced with " + key +
    396                                                                " from " + addressbook.getLocation());
     406                                                               ". From: " + addressbook.getLocation());
    397407                                                else
    398408                                                    log.append("Remove failed for: " + poldname +
    399409                                                               " to be replaced with " + key +
    400                                                                " from " + addressbook.getLocation());
     410                                                               ". From: " + addressbook.getLocation());
    401411                                            }
    402412                                            // now update the published addressbook
     
    410420                                        } else {
    411421                                            // mismatch, disallow
    412                                             if (log != null)
    413                                                 log.append("Action: " + action + " failed because" +
    414                                                            " destination for old name " + poldname +
    415                                                            " does not match new name " + key +
    416                                                            " from " + addressbook.getLocation());
    417                                             invalid++;
     422                                            logMismatch(log, action, key, pod, he.getDest(), addressbook);
    418423                                            continue;
    419424                                        }
     
    421426                                        if (log != null)
    422427                                            log.append("Action: " + action + " failed, missing required parameters" +
    423                                                        " from " + addressbook.getLocation());
     428                                                       ". From: " + addressbook.getLocation());
    424429                                        invalid++;
    425430                                        continue;
     
    447452                                                    log.append("Removed: " + poldname +
    448453                                                               " as requested" +
    449                                                                " from " + addressbook.getLocation());
     454                                                               ". From: " + addressbook.getLocation());
    450455                                                else
    451456                                                    log.append("Remove failed for: " + poldname +
    452457                                                               " as requested" +
    453                                                                " from " + addressbook.getLocation());
     458                                                               ". From: " + addressbook.getLocation());
    454459                                            }
    455460                                            // now update the published addressbook
     
    463468                                        } else if (pod2 != null) {
    464469                                            // mismatch, disallow
    465                                             if (log != null)
    466                                                 log.append("Action: " + action + " failed because" +
    467                                                            " destination for " + poldname +
    468                                                            " does not match" +
    469                                                            " from " + addressbook.getLocation());
     470                                            logMismatch(log, action, key, pod2, polddest, addressbook);
    470471                                            invalid++;
    471472                                        }
     
    473474                                        if (log != null)
    474475                                            log.append("Action: " + action + " failed, missing required parameters" +
    475                                                        " from " + addressbook.getLocation());
     476                                                       ". From: " + addressbook.getLocation());
    476477                                        invalid++;
    477478                                    }
     
    501502                                                        log.append("Removed: " + poldname +
    502503                                                                   " as requested" +
    503                                                                    " from " + addressbook.getLocation());
     504                                                                   ". From: " + addressbook.getLocation());
    504505                                                    else
    505506                                                        log.append("Remove failed for: " + poldname +
    506507                                                                   " as requested" +
    507                                                                    " from " + addressbook.getLocation());
     508                                                                   ". From: " + addressbook.getLocation());
    508509                                                }
    509510                                                // now update the published addressbook
     
    517518                                            } else if (pod2 != null) {
    518519                                                // mismatch, disallow
    519                                                 if (log != null)
    520                                                     log.append("Action: " + action + " failed because" +
    521                                                                " destination for " + poldname +
    522                                                                " does not match" +
    523                                                                " from " + addressbook.getLocation());
     520                                                logMismatch(log, action, key, pod2, polddest, addressbook);
    524521                                                invalid++;
    525522                                            }
     
    547544                                                    log.append("Removed: " + rev +
    548545                                                               " as requested" +
    549                                                                " from " + addressbook.getLocation());
     546                                                               ". From: " + addressbook.getLocation());
    550547                                                else
    551548                                                    log.append("Remove failed for: " + rev +
    552549                                                               " as requested" +
    553                                                                " from " + addressbook.getLocation());
     550                                                               ". From: " + addressbook.getLocation());
    554551                                            }
    555552                                            // now update the published addressbook
     
    565562                                        if (log != null)
    566563                                            log.append("Action: " + action + " failed, missing required parameters" +
    567                                                        " from " + addressbook.getLocation());
     564                                                       ". From: " + addressbook.getLocation());
    568565                                        invalid++;
    569566                                    }
     
    576573                                    if (log != null)
    577574                                        log.append("Action: " + action + " unrecognized" +
    578                                                    " from " + addressbook.getLocation());
     575                                                   ". From: " + addressbook.getLocation());
    579576                                    invalid++;
    580577                                    continue;
     
    603600                            nnew++;
    604601                        } else if (log != null) {
    605                             log.append("Bad hostname " + key + " from "
     602                            log.append("Bad hostname " + key + ". From: "
    606603                                   + addressbook.getLocation());
    607604                            invalid++;
     
    613610                            oldDest = router.lookup(key);
    614611                        if (oldDest != null && !oldDest.toBase64().equals(entry.getValue())) {
    615                             log.append("Conflict for " + key + " from "
     612                            log.append("Conflict for " + key + ". From: "
    616613                                       + addressbook.getLocation()
    617614                                       + ". Destination in remote address book is "
     
    646643    }
    647644
     645    private static void logMismatch(Log log, String action, String name, List<Destination> dests,
     646                                    String olddest, AddressBook addressbook) {
     647        if (log != null) {
     648            StringBuilder buf = new StringBuilder(16);
     649            final int sz = dests.size();
     650            for (int i = 0; i < sz; i++) {
     651                buf.append(dests.get(i).toBase64().substring(0, 6));
     652                if (i != sz - 1)
     653                    buf.append(", ");
     654            }
     655            log.append("Action: " + action + " failed because" +
     656                       " destinations for " + name +
     657                       " (" + buf + ')' +
     658                       " do not include" +
     659                       " (" + olddest.substring(0, 6) + ')' +
     660                       ". From: " + addressbook.getLocation());
     661        }
     662    }
     663
    648664    /**
    649665     * Run an update, using the Map settings to provide the parameters.
  • apps/addressbook/java/src/net/i2p/addressbook/HostTxtEntry.java

    r55de82b r75dd2251  
    2020import java.io.OutputStreamWriter;
    2121import java.io.StringWriter;
     22import java.util.Arrays;
    2223import net.i2p.data.Base32;
    2324import net.i2p.data.PrivateKeyFile;
     
    399400    }
    400401
     402    /**
     403     *  Usage: HostTxtEntry [-i] [-x] [hostname.i2p] [key=val]...
     404     */
    401405    public static void main(String[] args) throws Exception {
    402         int astart = 0;
    403         if (args.length > 0 && args[0].equals("-i"))
    404             astart++;
     406        boolean inner = false;
     407        boolean remove = false;
     408        if (args.length > 0 && args[0].equals("-i")) {
     409            inner = true;
     410            args = Arrays.copyOfRange(args, 1, args.length);
     411        }
     412        if (args.length > 0 && args[0].equals("-x")) {
     413            remove = true;
     414            args = Arrays.copyOfRange(args, 1, args.length);
     415        }
     416        String host;
     417        if (args.length > 0 && args[0].endsWith(".i2p")) {
     418            host = args[0];
     419            args = Arrays.copyOfRange(args, 1, args.length);
     420        } else {
     421            byte[] rand = new byte[5];
     422            RandomSource.getInstance().nextBytes(rand);
     423            host = Base32.encode(rand) + ".i2p";
     424        }
    405425        OrderedProperties props = new OrderedProperties();
    406         for (int i = astart; i < args.length; i++) {
     426        for (int i = 0; i < args.length; i++) {
    407427            int eq = args[i].indexOf("=");
    408428            props.setProperty(args[i].substring(0, eq), args[i].substring(eq + 1));
     
    413433        PrivateKeyFile pkf = new PrivateKeyFile(f);
    414434        pkf.createIfAbsent(SigType.EdDSA_SHA512_Ed25519);
    415         f.delete();
     435        //f.delete();
    416436        PrivateKeyFile pkf2;
    417         if (astart != 0) {
     437        if (inner) {
    418438            // inner
    419439            File f2 = new File("tmp-eepPriv2.dat");
    420440            pkf2 = new PrivateKeyFile(f2);
    421441            pkf2.createIfAbsent(SigType.DSA_SHA1);
    422             f2.delete();
     442            //f2.delete();
    423443            props.setProperty(PROP_OLDDEST, pkf2.getDestination().toBase64());
    424444        } else {
    425445            pkf2 = null;
    426446        }
    427         byte[] rand = new byte[5];
    428         RandomSource.getInstance().nextBytes(rand);
    429         String host = Base32.encode(rand) + ".i2p";
    430447        HostTxtEntry he = new HostTxtEntry(host, pkf.getDestination().toBase64(), props);
    431448        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
     
    434451        //out.flush();
    435452        SigningPrivateKey priv = pkf.getSigningPrivKey();
    436         if (astart != 0) {
     453        if (inner) {
    437454            SigningPrivateKey priv2 = pkf2.getSigningPrivKey();
    438455            he.signInner(priv2);
     
    444461        he.write(out);
    445462        out.flush();
    446         if (astart > 0 && !he.hasValidInnerSig())
     463        if (inner && !he.hasValidInnerSig())
    447464            throw new IllegalStateException("Inner fail 1");
    448465        if (!he.hasValidSig())
     
    456473        line = line.substring(line.indexOf(PROPS_SEPARATOR) + 2);
    457474        HostTxtEntry he2 = new HostTxtEntry(host, pkf.getDestination().toBase64(), line);
    458         if (astart > 0 && !he2.hasValidInnerSig())
     475        if (inner && !he2.hasValidInnerSig())
    459476            throw new IllegalStateException("Inner fail 2");
    460477        if (!he2.hasValidSig())
     
    462479
    463480        // 'remove' tests (corrupts earlier sigs)
    464         he.getProps().remove(PROP_SIG);
    465         he.signRemove(priv);
    466         //out.write("Remove entry:\n");
    467         sw = new StringWriter(1024);
    468         buf = new BufferedWriter(sw);
    469         he.writeRemove(buf);
    470         buf.flush();
    471         out.write(sw.toString());
    472         out.flush();
    473         line = sw.toString().substring(2).trim();
    474         HostTxtEntry he3 = new HostTxtEntry(line);
    475         if (!he3.hasValidRemoveSig())
    476             throw new IllegalStateException("Remove verify fail");
     481        if (remove) {
     482            he.getProps().remove(PROP_SIG);
     483            he.signRemove(priv);
     484            //out.write("Remove entry:\n");
     485            sw = new StringWriter(1024);
     486            buf = new BufferedWriter(sw);
     487            he.writeRemove(buf);
     488            buf.flush();
     489            out.write(sw.toString());
     490            out.flush();
     491            line = sw.toString().substring(2).trim();
     492            HostTxtEntry he3 = new HostTxtEntry(line);
     493            if (!he3.hasValidRemoveSig())
     494                throw new IllegalStateException("Remove verify fail");
     495        }
    477496
    478497        //out.write("Test passed\n");
  • core/java/src/net/i2p/client/naming/BlockfileNamingService.java

    r55de82b r75dd2251  
    1111import java.io.ByteArrayInputStream;
    1212import java.io.ByteArrayOutputStream;
     13import java.io.EOFException;
    1314import java.io.File;
    1415import java.io.FileInputStream;
     
    136137    private static final String DUMMY = "";
    137138    private static final int NEGATIVE_CACHE_SIZE = 32;
     139    private static final int MAX_VALUE_LENGTH = 4096;
    138140
    139141    /**
     
    853855                    DestEntry de = getEntry(list, key);
    854856                    if (de != null) {
    855                         int sz = de.destList.size();
    856                         // if any are invalid, assume they all are
    857                         boolean invalid = false;
    858                         for (int i = 0; i < sz; i++) {
    859                             if (!validate(key, de, listname))
    860                                 invalid = true;
     857                        if (!validate(key, de, listname))
     858                            continue;
     859                        if (de.destList != null) {
     860                            rv = de.destList;
     861                            if (storedOptions != null)
     862                                storedOptions.addAll(de.propsList);
     863                        } else {
     864                            rv = Collections.singletonList(de.dest);
     865                            if (storedOptions != null)
     866                                storedOptions.add(de.props);
    861867                        }
    862                         if (invalid)
    863                             continue;
    864                         rv = de.destList;
    865                         if (storedOptions != null)
    866                             storedOptions.addAll(de.propsList);
    867868                        break;
    868869                    }
     
    14921493            SigType type = d.getSigningPublicKey().getType();
    14931494            if (type != SigType.DSA_SHA1 && type.isAvailable()) {
    1494                 dests.add(0, d);
     1495                newDests.add(0, d);
    14951496                storedOptions.add(0, options);
    14961497            } else {
    1497                 dests.add(d);
     1498                newDests.add(d);
    14981499                storedOptions.add(options);
    14991500            }
     
    15631564                     de.dest != null &&
    15641565                     de.dest.getPublicKey() != null;
     1566        if (_isVersion4 && rv && de.destList != null) {
     1567            // additional checks for multi-dest
     1568            rv = de.propsList != null &&
     1569                 de.destList.size() == de.propsList.size() &&
     1570                 !de.destList.contains(null);
     1571        }
    15651572        if ((!rv) && (!_readOnly))
    15661573            _invalid.add(new InvalidEntry(key, listname));
     
    17071714     */
    17081715    private static class DestEntry {
    1709         /** may be null */
     1716        /** May be null.
     1717         *  If more than one dest, contains the first props.
     1718         */
    17101719        public Properties props;
    1711         /** may not be null */
     1720
     1721        /** May not be null.
     1722         *  If more than one dest, contains the first dest.
     1723         */
    17121724        public Destination dest;
    1713         /** may be null - v4 only - same size as destList - may contain null entries */
     1725
     1726        /** May be null - v4 only - same size as destList - may contain null entries
     1727         *  Only non-null if more than one dest.
     1728         *  First entry always equal to props.
     1729         */
    17141730        public List<Properties> propsList;
    1715         /** may be null - v4 only - same size as propsList */
     1731
     1732        /** May be null - v4 only - same size as propsList
     1733         *  Only non-null if more than one dest.
     1734         *  First entry always equal to dest.
     1735         */
    17161736        public List<Destination> destList;
    17171737
     
    17971817                    }
    17981818                    try {
    1799                         DataHelper.writeProperties(baos, p, true, false);
     1819                        writeProperties(baos, p);
    18001820                    } catch (DataFormatException dfe) {
    18011821                        logError("DB Write Fail - properties too big?", dfe);
     
    18201840                if (sz <= 0)
    18211841                    throw new DataFormatException("bad dest count " + sz);
    1822                 rv.props = DataHelper.readProperties(bais);
     1842                rv.props = readProperties(bais);
    18231843                rv.dest = Destination.create(bais);
    18241844                if (sz > 1) {
     
    18281848                    rv.destList.add(rv.dest);
    18291849                    for (int i = 1; i < sz; i++) {
    1830                         rv.propsList.add(DataHelper.readProperties(bais));
     1850                        rv.propsList.add(readProperties(bais));
    18311851                        rv.destList.add(Destination.create(bais));
    18321852                    }
     
    18411861            return rv;
    18421862        }
     1863    }
     1864
     1865    /**
     1866     * Same as DataHelper.writeProperties, UTF-8, unsorted,
     1867     * except that values may up to 4K bytes.
     1868     *
     1869     * @param props source may be null
     1870     * @throws DataFormatException if any key string is over 255 bytes long,
     1871     *                             if any value string is over 4096 bytes long, or if the total length
     1872     *                             (not including the two length bytes) is greater than 65535 bytes.
     1873     * @since 0.9.26
     1874     */
     1875    private static void writeProperties(ByteArrayOutputStream rawStream, Properties p)
     1876            throws DataFormatException, IOException {
     1877        if (p != null && !p.isEmpty()) {
     1878            ByteArrayOutputStream baos = new ByteArrayOutputStream(p.size() * 32);
     1879            for (Map.Entry<Object, Object> entry : p.entrySet()) {
     1880                String key = (String) entry.getKey();
     1881                String val = (String) entry.getValue();
     1882                DataHelper.writeStringUTF8(baos, key);
     1883                baos.write('=');
     1884                writeLongStringUTF8(baos, val);
     1885                baos.write(';');
     1886            }
     1887            if (baos.size() > 65535)
     1888                throw new DataFormatException("Properties too big (65535 max): " + baos.size());
     1889            byte propBytes[] = baos.toByteArray();
     1890            DataHelper.writeLong(rawStream, 2, propBytes.length);
     1891            rawStream.write(propBytes);
     1892        } else {
     1893            DataHelper.writeLong(rawStream, 2, 0);
     1894        }
     1895    }
     1896
     1897    /**
     1898     * Same as DataHelper.readProperties, UTF-8, unsorted,
     1899     * except that values may up to 4K bytes.
     1900     *
     1901     * Throws DataFormatException on duplicate key
     1902     *
     1903     * @param rawStream stream to read the mapping from
     1904     * @throws DataFormatException if the format is invalid
     1905     * @throws IOException if there is a problem reading the data
     1906     * @return a Properties
     1907     * @since 0.9.26
     1908     */
     1909    public static Properties readProperties(ByteArrayInputStream in)
     1910        throws DataFormatException, IOException {
     1911        Properties props = new Properties();
     1912        int size = (int) DataHelper.readLong(in, 2);
     1913        // this doesn't prevent reading past the end on corruption
     1914        int ignore = in.available() - size;
     1915        while (in.available() > ignore) {
     1916            String key = DataHelper.readString(in);
     1917            int b = in.read();
     1918            if (b != '=')
     1919                throw new DataFormatException("Bad key " + b);
     1920            String val = readLongString(in);
     1921            b = in.read();
     1922            if (b != ';')
     1923                throw new DataFormatException("Bad value");
     1924            Object old = props.put(key, val);
     1925            if (old != null)
     1926                throw new DataFormatException("Duplicate key " + key);
     1927        }
     1928        return props;
     1929    }
     1930
     1931    /**
     1932     * Same as DataHelper.writeStringUTF8, except that
     1933     * strings up to 4K bytes are allowed.
     1934     * Format is: one-byte length + data, or 0xff + two-byte length + data
     1935     *
     1936     * @param out stream to write string
     1937     * @param string to write out: null strings are valid, but strings of excess length will
     1938     *               cause a DataFormatException to be thrown
     1939     * @throws DataFormatException if the string is not valid
     1940     * @throws IOException if there is an IO error writing the string
     1941     */
     1942    private static void writeLongStringUTF8(ByteArrayOutputStream out, String string)
     1943        throws DataFormatException, IOException {
     1944        if (string == null) {
     1945            out.write(0);
     1946        } else {
     1947            byte[] raw = string.getBytes("UTF-8");
     1948            int len = raw.length;
     1949            if (len >= 255) {
     1950                if (len > MAX_VALUE_LENGTH)
     1951                    throw new DataFormatException(MAX_VALUE_LENGTH + " max, but this is "
     1952                                              + len + " [" + string + "]");
     1953                out.write(0xff);
     1954                DataHelper.writeLong(out, 2, len);
     1955            } else {
     1956                out.write(len);
     1957            }
     1958            out.write(raw);
     1959        }
     1960    }
     1961
     1962    /**
     1963     * Same as DataHelper.readString, except that
     1964     * strings up to 4K bytes are allowed.
     1965     * Format is: one-byte length + data, or 0xff + two-byte length + data
     1966     *
     1967     * @param in stream to read from
     1968     * @throws DataFormatException if the stream doesn't contain a validly formatted string
     1969     * @throws EOFException if there aren't enough bytes to read the string
     1970     * @throws IOException if there is an IO error reading the string
     1971     * @return UTF-8 string
     1972     */
     1973    private static String readLongString(ByteArrayInputStream in) throws DataFormatException, IOException {
     1974        int size = in.read();
     1975        if (size < 0)
     1976            throw new EOFException("EOF reading string");
     1977        if (size == 0xff) {
     1978            size = (int) DataHelper.readLong(in, 2);
     1979            if (size > MAX_VALUE_LENGTH)
     1980                throw new DataFormatException(MAX_VALUE_LENGTH + " max, but this is " + size);
     1981        }
     1982        if (size == 0)
     1983            return "";
     1984        byte raw[] = new byte[size];
     1985        int read = DataHelper.read(in, raw);
     1986        if (read != size)
     1987            throw new EOFException("EOF reading string");
     1988        return new String(raw, "UTF-8");
    18431989    }
    18441990
  • core/java/src/net/i2p/data/DataHelper.java

    r55de82b r75dd2251  
    5757 */
    5858public class DataHelper {
    59     private static final byte[] EQUAL_BYTES = getUTF8("=");
    60     private static final byte[] SEMICOLON_BYTES = getUTF8(";");
    6159
    6260    /**
     
    149147        if (read != size) throw new DataFormatException("Not enough data to read the properties, expected " + size + " but got " + read);
    150148        ByteArrayInputStream in = new ByteArrayInputStream(data);
    151         byte eqBuf[] = new byte[EQUAL_BYTES.length];
    152         byte semiBuf[] = new byte[SEMICOLON_BYTES.length];
    153149        while (in.available() > 0) {
    154150            String key = readString(in);
     
    156152            if (cached != null)
    157153                key = cached;
    158             read = read(in, eqBuf);
    159             if ((read != eqBuf.length) || (!eq(eqBuf, EQUAL_BYTES))) {
     154            int b = in.read();
     155            if (b != '=')
    160156                throw new DataFormatException("Bad key");
    161             }
    162157            String val = readString(in);
    163             read = read(in, semiBuf);
    164             if ((read != semiBuf.length) || (!eq(semiBuf, SEMICOLON_BYTES))) {
     158            b = in.read();
     159            if (b != ';')
    165160                throw new DataFormatException("Bad value");
    166             }
    167161            Object old = props.put(key, val);
    168162            if (old != null)
     
    183177     *
    184178     * @param rawStream stream to write to
    185      * @param props properties to write out
     179     * @param props properties to write out, may be null
    186180     * @throws DataFormatException if there is not enough valid data to write out,
    187181     *                             or a length limit is exceeded
     
    240234    public static void writeProperties(OutputStream rawStream, Properties props, boolean utf8, boolean sort)
    241235            throws DataFormatException, IOException {
    242         if (props != null) {
     236        if (props != null && !props.isEmpty()) {
    243237            Properties p;
    244238            if (sort) {
     
    256250                else
    257251                    writeString(baos, key);
    258                 baos.write(EQUAL_BYTES);
     252                baos.write('=');
    259253                if (utf8)
    260254                    writeStringUTF8(baos, val);
    261255                else
    262256                    writeString(baos, val);
    263                 baos.write(SEMICOLON_BYTES);
     257                baos.write(';');
    264258            }
    265259            if (baos.size() > 65535)
     
    302296                String val = (String) entry.getValue();
    303297                writeStringUTF8(baos, key);
    304                 baos.write(EQUAL_BYTES);
     298                baos.write('=');
    305299                writeStringUTF8(baos, val);
    306                 baos.write(SEMICOLON_BYTES);
     300                baos.write(';');
    307301            }
    308302            if (baos.size() > 65535)
     
    336330        offset += 2;
    337331        ByteArrayInputStream in = new ByteArrayInputStream(source, offset, size);
    338         byte eqBuf[] = new byte[EQUAL_BYTES.length];
    339         byte semiBuf[] = new byte[SEMICOLON_BYTES.length];
    340332        while (in.available() > 0) {
    341333            String key;
     
    345337                if (cached != null)
    346338                    key = cached;
    347                 int read = read(in, eqBuf);
    348                 if ((read != eqBuf.length) || (!eq(eqBuf, EQUAL_BYTES))) {
     339                int b = in.read();
     340                if (b != '=')
    349341                    throw new DataFormatException("Bad key");
    350                 }
    351342            } catch (IOException ioe) {
    352343                throw new DataFormatException("Bad key", ioe);
     
    355346            try {
    356347                val = readString(in);
    357                 int read = read(in, semiBuf);
    358                 if ((read != semiBuf.length) || (!eq(semiBuf, SEMICOLON_BYTES))) {
     348                int b = in.read();
     349                if (b != ';')
    359350                    throw new DataFormatException("Bad value");
    360                 }
    361351            } catch (IOException ioe) {
    362352                throw new DataFormatException("Bad value", ioe);
     
    911901     * @throws DataFormatException if the string is not valid
    912902     * @throws IOException if there is an IO error writing the string
    913      */
    914     private static void writeStringUTF8(OutputStream out, String string)
     903     * @since public since 0.9.26
     904     */
     905    public static void writeStringUTF8(OutputStream out, String string)
    915906        throws DataFormatException, IOException {
    916907        if (string == null) {
     
    937928     * @deprecated unused
    938929     */
     930    @Deprecated
    939931    public static Boolean readBoolean(InputStream in) throws DataFormatException, IOException {
    940932        int val = in.read();
Note: See TracChangeset for help on using the changeset viewer.