1 /*
   2  * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 package com.sun.jmx.snmp;
  28 
  29 
  30 /**
  31  * The <CODE>BerEncoder</CODE> class is used for encoding data using BER.
  32  *
  33  * A <CODE>BerEncoder</CODE> needs to be set up with a byte buffer. The encoded
  34  * data are stored in this byte buffer.
  35  * <P>
  36  * NOTE : the buffer is filled from end to start. This means the caller
  37  * needs to encode its data in the reverse order.
  38  *
  39  *
  40  * <p><b>This API is a Sun Microsystems internal API  and is subject
  41  * to change without notice.</b></p>
  42  *
  43  * @since 1.5
  44  */
  45 
  46 public class BerEncoder {
  47 
  48   /**
  49   * Constructs a new encoder and attaches it to the specified byte string.
  50   *
  51   * @param b The byte string containing the encoded data.
  52   */
  53 
  54   public BerEncoder(byte b[]) {
  55     bytes = b ;
  56     start = b.length ;
  57     stackTop = 0 ;
  58   }
  59 
  60 
  61   /**
  62   * Trim the encoding data and returns the length of the encoding.
  63   *
  64   * The encoder does backward encoding : so the bytes buffer is
  65   * filled from end to start. The encoded data must be shift before
  66   * the buffer can be used. This is the purpose of the <CODE>trim</CODE> method.
  67   *
  68   * After a call to the <CODE>trim</CODE> method, the encoder is reinitialized and <CODE>putXXX</CODE>
  69   * overwrite any existing encoded data.
  70   *
  71   * @return The length of the encoded data.
  72   */
  73 
  74   public int trim() {
  75     final int result = bytes.length - start ;
  76 
  77     // for (int i = start ; i < bytes.length ; i++) {
  78     //  bytes[i-start] = bytes[i] ;
  79     // }
  80     if (result > 0)
  81         java.lang.System.arraycopy(bytes,start,bytes,0,result);
  82 
  83     start = bytes.length ;
  84     stackTop = 0 ;
  85 
  86     return result ;
  87   }
  88 
  89   /**
  90   * Put an integer.
  91   *
  92   * @param v The integer to encode.
  93   */
  94 
  95   public void putInteger(int v) {
  96     putInteger(v, IntegerTag) ;
  97   }
  98 
  99 
 100   /**
 101   * Put an integer with the specified tag.
 102   *
 103   * @param v The integer to encode.
 104   * @param tag The tag to encode.
 105   */
 106 
 107   public void putInteger(int v, int tag) {
 108     putIntegerValue(v) ;
 109     putTag(tag) ;
 110   }
 111 
 112 
 113 
 114   /**
 115   * Put an integer expressed as a long.
 116   *
 117   * @param v The long to encode.
 118   */
 119 
 120   public void putInteger(long v) {
 121     putInteger(v, IntegerTag) ;
 122   }
 123 
 124 
 125   /**
 126   * Put an integer expressed as a long with the specified tag.
 127   *
 128   * @param v The long to encode
 129   * @param tag The tag to encode.
 130   */
 131 
 132   public void putInteger(long v, int tag) {
 133     putIntegerValue(v) ;
 134     putTag(tag) ;
 135   }
 136 
 137 
 138 
 139   /**
 140   * Put an octet string.
 141   *
 142   * @param s The bytes to encode
 143   */
 144 
 145   public void putOctetString(byte[] s) {
 146     putOctetString(s, OctetStringTag) ;
 147   }
 148 
 149 
 150   /**
 151   * Put an octet string with a specified tag.
 152   *
 153   * @param s The bytes to encode
 154   * @param tag The tag to encode.
 155   */
 156 
 157   public void putOctetString(byte[] s, int tag) {
 158     putStringValue(s) ;
 159     putTag(tag) ;
 160   }
 161 
 162 
 163   /**
 164   * Put an object identifier.
 165   *
 166   * @param s The oid to encode.
 167   */
 168 
 169   public void putOid(long[] s) {
 170     putOid(s, OidTag) ;
 171   }
 172 
 173 
 174   /**
 175   * Put an object identifier with a specified tag.
 176   *
 177   * @param s The integer to encode.
 178   * @param tag The tag to encode.
 179   */
 180 
 181   public void putOid(long[] s, int tag) {
 182     putOidValue(s) ;
 183     putTag(tag) ;
 184   }
 185 
 186 
 187   /**
 188   * Put a <CODE>NULL</CODE> value.
 189   */
 190 
 191   public void putNull() {
 192     putNull(NullTag) ;
 193   }
 194 
 195 
 196   /**
 197   * Put a <CODE>NULL</CODE> value with a specified tag.
 198   *
 199   * @param tag The tag to encode.
 200   */
 201 
 202   public void putNull(int tag) {
 203     putLength(0) ;
 204     putTag(tag) ;
 205   }
 206 
 207 
 208 
 209   /**
 210   * Put an <CODE>ANY</CODE> value. In fact, this method does not encode anything.
 211   * It simply copies the specified bytes into the encoding.
 212   *
 213   * @param s The encoding of the <CODE>ANY</CODE> value.
 214   */
 215 
 216   public void putAny(byte[] s) {
 217         putAny(s, s.length) ;
 218   }
 219 
 220 
 221   /**
 222   * Put an <CODE>ANY</CODE> value. Only the first <CODE>byteCount</CODE> are considered.
 223   *
 224   * @param s The encoding of the <CODE>ANY</CODE> value.
 225   * @param byteCount The number of bytes of the encoding.
 226   */
 227 
 228   public void putAny(byte[] s, int byteCount) {
 229       java.lang.System.arraycopy(s,0,bytes,start-byteCount,byteCount);
 230       start -= byteCount;
 231       //    for (int i = byteCount - 1 ; i >= 0 ; i--) {
 232       //      bytes[--start] = s[i] ;
 233       //    }
 234   }
 235 
 236 
 237   /**
 238   * Open a sequence.
 239   * The encoder push the current position on its stack.
 240   */
 241 
 242   public void openSequence() {
 243     stackBuf[stackTop++] = start ;
 244   }
 245 
 246 
 247   /**
 248   * Close a sequence.
 249   * The decode pull the stack to know the end of the current sequence.
 250   */
 251 
 252   public void closeSequence() {
 253     closeSequence(SequenceTag) ;
 254   }
 255 
 256 
 257   /**
 258   * Close a sequence with the specified tag.
 259   */
 260 
 261   public void closeSequence(int tag) {
 262     final int end = stackBuf[--stackTop] ;
 263     putLength(end - start) ;
 264     putTag(tag) ;
 265   }
 266 
 267 
 268   //
 269   // Some standard tags
 270   //
 271   public final static int BooleanTag      = 1 ;
 272   public final static int IntegerTag      = 2 ;
 273   public final static int OctetStringTag  = 4 ;
 274   public final static int NullTag          = 5 ;
 275   public final static int OidTag          = 6 ;
 276   public final static int SequenceTag      = 0x30 ;
 277 
 278 
 279 
 280 
 281   ////////////////////////// PROTECTED ///////////////////////////////
 282 
 283 
 284 
 285   /**
 286   * Put a tag and move the current position backward.
 287   *
 288   * @param tag The tag to encode.
 289   */
 290 
 291   protected final void putTag(int tag) {
 292     if (tag < 256) {
 293       bytes[--start] = (byte)tag ;
 294     }
 295     else {
 296       while (tag != 0) {
 297         bytes[--start] = (byte)(tag & 127) ;
 298         tag = tag << 7 ;
 299       }
 300     }
 301   }
 302 
 303 
 304   /**
 305   * Put a length and move the current position backward.
 306   *
 307   * @param length The length to encode.
 308   */
 309 
 310   protected final void putLength(final int length) {
 311     if (length < 0) {
 312       throw new IllegalArgumentException() ;
 313     }
 314     else if (length < 128) {
 315       bytes[--start] = (byte)length ;
 316     }
 317     else if (length < 256) {
 318       bytes[--start] = (byte)length ;
 319       bytes[--start] = (byte)0x81 ;
 320     }
 321     else if (length < 65536) {
 322       bytes[--start] = (byte)(length) ;
 323       bytes[--start] = (byte)(length >> 8) ;
 324       bytes[--start] = (byte)0x82 ;
 325     }
 326     else if (length < 16777126) {
 327       bytes[--start] = (byte)(length) ;
 328       bytes[--start] = (byte)(length >> 8) ;
 329       bytes[--start] = (byte)(length >> 16) ;
 330       bytes[--start] = (byte)0x83 ;
 331     }
 332     else {
 333       bytes[--start] = (byte)(length) ;
 334       bytes[--start] = (byte)(length >> 8) ;
 335       bytes[--start] = (byte)(length >> 16) ;
 336       bytes[--start] = (byte)(length >> 24) ;
 337       bytes[--start] = (byte)0x84 ;
 338     }
 339   }
 340 
 341 
 342   /**
 343   * Put an integer value and move the current position backward.
 344   *
 345   * @param v The integer to encode.
 346   */
 347 
 348   protected final void putIntegerValue(int v) {
 349     final int end = start ;
 350     int mask = 0x7f800000 ;
 351     int byteNeeded = 4 ;
 352     if (v < 0) {
 353       while (((mask & v) == mask) && (byteNeeded > 1)) {
 354         mask = mask >> 8 ;
 355         byteNeeded-- ;
 356       }
 357     }
 358     else {
 359       while (((mask & v) == 0) && (byteNeeded > 1)) {
 360         mask = mask >> 8 ;
 361         byteNeeded-- ;
 362       }
 363     }
 364     for (int i = 0 ; i < byteNeeded ; i++) {
 365       bytes[--start] = (byte)v ;
 366       v =  v >> 8 ;
 367     }
 368     putLength(end - start) ;
 369   }
 370 
 371 
 372   /**
 373   * Put an integer value expressed as a long.
 374   *
 375   * @param v The integer to encode.
 376   */
 377 
 378   protected final void putIntegerValue(long v) {
 379     final int end = start ;
 380     long mask = 0x7f80000000000000L ;
 381     int byteNeeded = 8 ;
 382     if (v < 0) {
 383       while (((mask & v) == mask) && (byteNeeded > 1)) {
 384         mask = mask >> 8 ;
 385         byteNeeded-- ;
 386       }
 387     }
 388     else {
 389       while (((mask & v) == 0) && (byteNeeded > 1)) {
 390         mask = mask >> 8 ;
 391         byteNeeded-- ;
 392       }
 393     }
 394     for (int i = 0 ; i < byteNeeded ; i++) {
 395       bytes[--start] = (byte)v ;
 396       v =  v >> 8 ;
 397     }
 398     putLength(end - start) ;
 399   }
 400 
 401 
 402   /**
 403   * Put a byte string and move the current position backward.
 404   *
 405   * @param s The byte string to encode.
 406   */
 407 
 408   protected final void putStringValue(byte[] s) {
 409       final int datalen = s.length;
 410       java.lang.System.arraycopy(s,0,bytes,start-datalen,datalen);
 411       start -= datalen;
 412       // for (int i = s.length - 1 ; i >= 0 ; i--) {
 413       //   bytes[--start] = s[i] ;
 414       // }
 415       putLength(datalen) ;
 416   }
 417 
 418 
 419 
 420   /**
 421   * Put an oid and move the current position backward.
 422   *
 423   * @param s The oid to encode.
 424   */
 425 
 426   protected final void putOidValue(final long[] s) {
 427       final int end = start ;
 428       final int slength = s.length;
 429 
 430       // bugId 4641746: 0, 1, and 2 are legal values.
 431       if ((slength < 2) || (s[0] > 2) || (s[1] >= 40)) {
 432           throw new IllegalArgumentException() ;
 433       }
 434       for (int i = slength - 1 ; i >= 2 ; i--) {
 435           long c = s[i] ;
 436           if (c < 0) {
 437               throw new IllegalArgumentException() ;
 438           }
 439           else if (c < 128) {
 440               bytes[--start] = (byte)c ;
 441           }
 442           else {
 443               bytes[--start] = (byte)(c & 127) ;
 444               c = c >> 7 ;
 445               while (c != 0) {
 446                   bytes[--start] = (byte)(c | 128) ;
 447                   c = c >> 7 ;
 448               }
 449           }
 450       }
 451       bytes[--start] = (byte)(s[0] * 40 + s[1]) ;
 452       putLength(end - start) ;
 453   }
 454 
 455 
 456   //
 457   // This is the byte array containing the encoding.
 458   //
 459   protected final byte bytes[];
 460 
 461   //
 462   // This is the index of the first byte of the encoding.
 463   // It is initialized to <CODE>bytes.length</CODE> and decrease each time
 464   // an value is put in the encoder.
 465   //
 466   protected int start = -1 ;
 467 
 468   //
 469   // This is the stack where end of sequences are kept.
 470   // A value is computed and pushed in it each time the <CODE>openSequence</CODE> method
 471   // is invoked.
 472   // A value is pulled and checked each time the <CODE>closeSequence</CODE> method is called.
 473   //
 474   protected final int stackBuf[] = new int[200] ;
 475   protected int stackTop = 0 ;
 476 
 477 }