/**
 * Compose various SLPv2 messages (protocol stack)
 * Return the message in a byte array
 *
 */

package eu.artemis.shield.discovery.slpdaemon.impl;

import java.io.*;
import java.util.*;

public class slpMsgComposer {

    DataOutputStream      d;
    ByteArrayOutputStream b;

    slpMsgComposer() {
        b = new ByteArrayOutputStream();
        d = new DataOutputStream(b);
    }

/**
 * Compose SLP common message header, flags may be set, there
 * may exist extensions, language tag is NOT included here
 *+--------------+-----------------+----------------------------------+
 *|  Version     |   Function-ID   |            Length                |
 *+--------------+-+-+-+-----------+---------------+------------------+
 *| Length cont. |O|F|R|       Reserved            | Next Ext. Offset |
 *+--------------+-+-+-+-----------+---------------+------------------+
 *|     Next Ext. Offset  Cont.    |              XID                 |
 *+--------------------------------+----------------------------------+
 */
    private void Header(int type, int len, int flag, int xid) {
        try {
            b.reset();
            d.writeByte(Const.version)// SLP version
            d.writeByte(type);          // Function type
            d.writeByte(0);             // len
            d.writeShort(len);          // length
            d.writeShort(flag);         // flag bits
            d.writeByte(0);             // next ext. offset
            d.writeShort(0);            // next ext. offset
            d.writeShort(xid);          // XID
        catch (Exception e) {
            da.append(e);
        }
    }

/**
 * Put a string in the byte[], precede with its length.
 * The string could be empty, with a length of 0.
 * If the string is null, then no action is taken.
 */
    private void putString(String s) {
        if (s == nullreturn;
        try {
            d.writeShort(s.length());
            if (s.length() 0) {
                d.writeBytes(s);
            }
        catch (Exception e) {
          da.append(e);
        }
    }

/**
 * put an integer as a byte (normally zero) in the byte[]
 */
    private void putByte(int z) {
        try {
            d.writeByte(z);
        catch (Exception e) {
          da.append(e.toString());
        }
    }

/**
 * put an integer as a short (normally as error code) in the byte[]
 */
    private void putShort(int z) {
        try {
            d.writeShort(z);
        catch (Exception e) { 
          da.append(e.toString());
        }
    }

/**
 * put an integer as an integer in the byte[]
 */
    private void putInt(int z) {
        try {
            d.writeInt(z);
        catch (Exception e) {
          da.appendDebug("slpMsgComposer::putInt()");
          da.appendDebug(e);
        }
    }

    private void putLong(long z) {
        try {
            d.writeLong(z);
        catch (Exception e) {
          da.appendDebug("slpMsgComposer::putLong()");
          da.appendDebug(e);
        }
    }

/**
 * put URL entry in the byte[], assume "# of URL auths" is zero 
 *+---------------+---------------------------------+----------------+
 *|    Reserved   |          Lifetime               |   URL length   |
 *+---------------+---------------------------------+----------------+
 *| URL len cont. |          URL (variable length)                   \
 *+---------------+--------------------------------------------------+
 *| # of URL auths|          Auth. blocks (if any)                   \
 *+------------------------------------------------------------------+
 */
    private void putURL(String url, int lifetime) {
        try {
            d.writeByte(0);                     // reserved
            d.writeShort(lifetime);             // lifetime
            d.writeShort(url.length());         // len of URL
            d.writeBytes(url);                  // URL string
            d.writeByte(0);                     // # of authenticate
        catch (Exception e) {
          da.append(e.toString());
        }
    }

/**
 * calculate string length, precede with a short integer length field
 */
    private int strlen(String s
    {
      if (s != null)
      {
        return (+ s.length());
      }
        return 2;
    }

/**
 * calculate URL-entry length, assume "# of url auths" is zero
 */
    private int urllen(String url) {
        return (+ url.length());
    }

/**
 * service request <#1>
 *+----------------------------+---------------------------+
 *| Length of <PRList>         |   <PRList> string         \
 *+----------------------------+---------------------------+
 *| Length of <service-type>   <service-type> string     \
 *+----------------------------+---------------------------+
 *| Length of <scope-list>     <scope-list> string       \
 *+----------------------------+---------------------------+
 *| Length of predicate string | service request predicate \
 *+----------------------------+---------------------------+
 *| Length of <SLP SPI> string |   <SLP SPI> string        \
 *+----------------------------+---------------------------+
 */
    public byte[] SrvRqst(int xid, int flag, String ltag, String pr, 
                String type, String scope, String pred, String spi) {
        int len = Const.header_len + strlen(ltag+ strlen(pr
                strlen(type+ strlen(scope+ strlen(pred+ strlen(spi);
        Header(Const.SrvRqst, len, flag, xid);
        putString(ltag);                // language tag
        putString(pr);                  // PRList
        putString(type);                // service type
        putString(scope);               // scope list
        putString(pred);                // predicate
        putString(spi);                 // SPI
        if (Const.MESSAGE_LOG_ENABLEDda.append("\nOUTGOING SRVRQST MESSAGE" +
        "\n- Xid = "+ xid +
        "\n- Ltag = "+ ltag +
        "\n- PRList = "+ pr +
        "\n- Type = " + type +
        "\n- Scope = " + scope +                            
        "\n- Predicate = " + pred + 
        "\n- SPI = " + spi +                    
        "\n");  
        return b.toByteArray();
    }

/**
 * service reply (reply for service request) <#2>
 *+----------------------------+---------------------------+
 *|       Error Code           |    URL entry count        |
 *+----------------------------+---------------------------+
 *|   <URl entry 1>           ...     <URL entry N>        \
 *+----------------------------+---------------------------+
 */
    public byte[] SrvReply(int xid, String ltag, byte[] buf) {
        int len = Const.header_len + strlen(ltag+ buf.length;
        Header(Const.SrvRply, len, Const.normal_flag, xid);
        putString(ltag);                        // language tag
        try {
            d.write(buf, 0, buf.length);        // ErrCode + #URL + each entry
        catch (Exception e) {
          da.append(e.toString());
        }
        if (Const.MESSAGE_LOG_ENABLEDda.append("\nOUTGOING SRVREPLY MESSAGE" +
        "\n- Xid = "+ xid +
        "\n- Ltag = "+ ltag +        
        "\n")
      //  if (Const.OSGI_BUNDLE) daOSGi.visual_log("(PSM): SLP SERVICE DISCOVERY RESPONSE");
        return b.toByteArray();
    }

    /**
     * secure service reply (reply for secure service request) <#14>
     *+----------------------------+---------------------------+
     *|       Error Code           |    URL entry count        |
     *+----------------------------+---------------------------+
     *|   <URl entry 1>           ...     <URL entry N>        \
     *+----------------------------+---------------------------+
     */ 
        public byte[] SrvReplyAuth(int xid, String ltag, byte[] buf) {
            int len = Const.header_len + strlen(ltag+ buf.length;
            int ecode=0;
            Header(Const.SrvRplyAuth, len, Const.normal_flag, xid);
            putString(ltag);                        // language tag
            try {
                
              d.write(buf, 0, buf.length);        // ErrCode + #URL + each entry
                for (int i=0; i<2; i++) {
                    ecode <<=  8;
                    ecode += buf[i0xff;
                }
            catch (Exception e) {
              da.appendDebug(e);
            }
            if (ecode==Const.AUTHENTICATION_FAILEDda.displayMessage ("Authentication Failed", Const.EXCLAMATION, "Authentication check");
            if (ecode==Const.AUTHENTICATION_ABSENTda.displayMessage ("Authentication Absent", Const.EXCLAMATION, "Authentication check");
          
            if (Const.MESSAGE_LOG_ENABLEDda.append("\nOUTGOING SRVREPLYAUTH MESSAGE" +
            "\n- Xid = "+ xid +
            "\n- Ltag = "+ ltag +        
            "\n- Ecode= "+ ecode +
                    "\n")
          //  if (Const.OSGI_BUNDLE) daOSGi.visual_log("(PSM): SLP SERVICE DISCOVERY RESPONSE");
            return b.toByteArray();
        }    
    
/**
 * service registration <#3>
 *+----------------------------------------------------------------+
 *|                          <URL-Entry>                           \
 *+---------------------------------+------------------------------+
 *|  Length of service type string  |     <service-type>           \
 *+---------------------------------+------------------------------+
 *|  Length of <scope-list>         |     <scope-list>             \
 *+---------------------------------+------------------------------+
 *|  Length of attr-list string     |     <attr-list>              \
 *+----------------+----------------+------------------------------+
 *| # of AttrAuths | (if present) Attribute Authentication Blocks  \
 *+----------------+-----------------------------------------------+
 */
    public byte[] SrvReg(int xid, int flag, String ltag, String url, 
                int lifetime, String type, String scope, String attr) {
        int len = Const.header_len + strlen(ltag+ urllen(url+
                  strlen(type+ strlen(scope+ strlen(attr1;
        Header(Const.SrvReg, len, flag, xid);
        putString(ltag);                         // language tag
        putURL(url, lifetime);
        putString(type);                         // service type
        putString(scope);                        // scope list
        putString(attr);                         // attr list
        putByte(0);                              // num of attrAuths
        if (Const.MESSAGE_LOG_ENABLEDda.append("\nOUTGOING SRVREG MESSAGE"+
        "\n- Xid = "+ xid + 
        "\n- Ltag = "+ ltag +
        "\n- Type = " + type + 
        "\n- Scope = " + scope + 
        "\n- Url = " + url + 
        "\n- Lifetime = " + lifetime +                           
        "\n- Attribute List = " + attr + 
        "\n");
        return b.toByteArray();
    }

/**
 * service De-registration <#4>
 *+-----------------------------+---------------------------+
 *|  Length of <scope-list>     |        <scope-list>       \
 *+-----------------------------+---------------------------+
 *|                         <URL-entry>                     \
 *+-----------------------------+---------------------------+
 *|  Length of <tag-list>       |        <tag-list>         \
 *+-----------------------------+---------------------------+
 */
    public byte[] SrvDeReg(int xid, String ltag, String scope, String url, 
                int ltime, String tag) {
        int len = Const.header_len + strlen(ltag+ strlen(scope+
                        urllen(url+ strlen(tag);
        Header(Const.SrvDeReg, len, Const.normal_flag, xid);
        putString(ltag);                         // language tag
        putString(scope);                        // scope list
        putURL(url, ltime);                      // URL
        putString(tag);                          // tag list
        if (Const.MESSAGE_LOG_ENABLEDda.append("\nOUTGOING SRVDEREG MESSAGE" +
        "\n- Xid = "+ xid + 
        "\n- Ltag = "+ ltag +
        "\n- Scope = " + scope +
        "\n- Url = " + url + 
        "\n- Tag List = " + tag + 
        "\n");
        return b.toByteArray();
    }

/**
 * service ack (reply for SrvReg & SrvDeReg) <#5>
 *+-----------------------------+
 *|        Error Code           |
 *+-----------------------------+
 */
    public byte[] SrvAck(int xid, String ltag, int errcode) {
        int len = Const.header_len + strlen(ltag2;
        Header(Const.SrvAck, len, Const.normal_flag, xid);
        putString(ltag);                        // language tag
        putShort(errcode);                      // ErrCode
        if (Const.MESSAGE_LOG_ENABLEDda.append("\nOUTGOING SRVACK MESSAGE" +
        "\n- Xid = "+ xid + 
        "\n- Ltag = "+ ltag +
        "\n- Error = " + errcode +
        "\n");                
       // if (Const.OSGI_BUNDLE) daOSGi.visual_log("(PSM): SLP SERVICE REGISTRATION ACK");
        return b.toByteArray();        
    }

/** 
 * attribute request <#6>
 *+-------------------------------+----------------------------+
 *|  Length of PRList             |   <PRList> string          |
 *+-------------------------------+----------------------------+
 *|  Length of URL                |         URL                |
 *+-------------------------------+----------------------------+
 *|  Length of <scope-list>       |   <scope-list> string      |
 *+-------------------------------+----------------------------+
 *|  Length of <tag-list> string  |   <tag-list> string        |
 *+-------------------------------+----------------------------+
 *|  Length of <SLP SPI> string   |   <SLP SPI> string         |
 *+-------------------------------+----------------------------+
 */
    public byte[] AttrRqst(int xid, String ltag, String pr, String url,
                           String scope, String tag, String spi) { 
        int len = Const.header_len + strlen(ltag+ strlen(pr
                strlen(url+ strlen(scope+ strlen(tag+ strlen(spi);
        Header(Const.AttrRqst, len, Const.normal_flag, xid);
        putString(ltag);
        putString(pr);
        putString(url);
        putString(scope);
        putString(tag);
        putString(spi);
        if (Const.MESSAGE_LOG_ENABLEDda.append("\nOUTGOING ATTRRQST MESSAGE"+
        "\n- Xid = "+ xid + 
        "\n- Ltag = "+ ltag +
        "\n- PRLIst = " + pr +        
        "\n- Url = " + url +
        "\n- Scope = " + scope + 
        "\n- Tag List = " + tag +
        "\n- SPI = " + spi + 
        "\n")
        return b.toByteArray();
    }

/**
 * attribute reply (reply for attribute request) <#7>
 *+-----------------------------+---------------------------------+
 *|         Error Code          |   Length of <attr-list>         |
 *+-----------------------------+---------------------------------+
 *|                          <attr-list>                          \
 *+----------------+----------------------------------------------+
 *| # of AttrAuths |  Attribute authentication block (if present) \
 *+----------------+----------------------------------------------+
 */
    public byte[] AttrReply(int xid, String ltag, int ecode, String buf) {
        int len = Const.header_len + strlen(ltag+ strlen(buf)
        Header(Const.AttrRply, len, Const.normal_flag, xid);
        putString(ltag);                // language tag
        putShort(ecode);                // ErrCode
        putString(buf);                 // attr-list
        putByte(0);                     // # of AttrAuths
        if (Const.MESSAGE_LOG_ENABLED) {
          if (Const.FORMAT_MESSAGE_LOG_ENABLED) {
            String temp = "";
            int i = 0;
            StringTokenizer st = new StringTokenizer(buf,")");
            while (st.hasMoreTokens()) {
              if (i!=0
                temp += st.nextToken().substring(2"\n                   ";
              else 
                temp += st.nextToken().substring(1"\n                   ";
              i++;
            }
            buf = temp;
          }
          da.append("\nOUTGOING ATTRREPLY MESSAGE"+      
        "\n- Xid = "+ xid + 
        "\n- Ltag = "+ ltag +
        "\n- Error = " + ecode +         
        "\n- Attribute List = " + buf + 
        "\n");  
        }
        return b.toByteArray();
    }

/**
 * directory agent advertisement <#8>
 *+-------------------------------+--------------------------------+
 *|     Error Code                |   DA Stateless Boot Timestamp  |
 *+-------------------------------+--------------------------------+
 *| DA Stateless Boot Time cont.  |      Length of URL             |
 *+-------------------------------+--------------------------------+
 *|                              URL                               \
 *+-------------------------------+--------------------------------+
 *|   Length of <scope-list>      |      <scope-list>              \
 *+-------------------------------+--------------------------------+
 *|   Length of <attr-list>       |      <attr-list>               \
 *+-------------------------------+--------------------------------+
 *|   Length of SLP <SPI>         |      SLP <SPI> string          \
 *+---------------+---------------+--------------------------------+
 *| # Auth Blocks |       Authentication blocl (if any)            \
 *+---------------+---------------+--------------------------------+
 */
    public byte[] DAAdvert(int xid, int flag, String ltag, int ts, 
                String url, String scope, String attr, String spi) {
        int len = Const.header_len + + strlen(ltag+ strlen(url
                strlen(scope+ strlen(attr+ strlen(spi)
        Header(Const.DAAdvert, len, flag, xid);
        putString(ltag);                // language tag
        putShort(0);                    // ErrCode
        putInt(ts);                     // boot timestamp        
        putString(url);                 // URL
        putString(scope);               // scope list
        putString(attr);                // attribute list
        putString(spi);                 // SLP SPI
        putByte(0);                     // # Auth blocks
        if (Const.MESSAGE_LOG_ENABLED && Const.DAAdvert_LOG_ENABLEDda.append("\nOUTGOING DAADVERT MESSAGE"+
        "\n- Xid = "+ xid + 
        "\n- Ltag = "+ ltag +
        "\n- Boot TS = "+ ts +
        "\n- Url = " + url + 
        "\n- Scope = " + scope + 
        "\n- Attribute List = " + attr +
        "\n- SPI = " + spi +
        "\n");
        return b.toByteArray();
    }

/**
 * service type request <#9>
 *+-------------------------------+-----------------------------+
 *|     Length of PRList          |     <PRList> string         |
 *+-------------------------------+-----------------------------+
 *| Length of Naming Authority    |  <Naming Authority String>  |
 *+-------------------------------+-----------------------------+
 *| Length of <scope-list>        |    <scope-list> string      |
 *+-------------------------------+-----------------------------+
 */
    public byte[] SrvTypeRqst(int xid, String ltag, String pr, String na, 
                                String scope) {
        int len;
        if (na.equals("-1")) {
            len = Const.header_len + strlen(ltag+ strlen(pr
                        + strlen(scope);
        else {
            len = Const.header_len + strlen(ltag+ strlen(pr
                        strlen(na+ strlen(scope);
        }
        Header(Const.SrvTypeRqst, len, Const.normal_flag, xid);
        putString(ltag);                // language tag
        putString(pr);                  // PRList
        if (na.equals("-1")) {          // Naming Authority
            putShort(0xFFFF);           // -1 for all
        else {
            putString(na);                      
        }
        putString(scope);               // scope list
        if (Const.MESSAGE_LOG_ENABLEDda.append("\nOUTGOING SRVTYPERQST MESSAGE"+
        "\n- Xid = "+ xid + 
        "\n- Ltag = "+ ltag +
        "\n- PRList = " + pr + 
        "\n- Naming Authority = " + na + 
        "\n- Scope = " + scope + 
        "\n");   
        return b.toByteArray();
    }

/**
 * service type reply (reply for service type request) <#10>
 *+-------------------------------+-------------------------------+
 *|          Error Code           |    Length of <srvType-list>   |
 *+-------------------------------+-------------------------------+
 *|                       <srvType-list>                          \
 *+---------------------------------------------------------------+
 */
    public byte[] SrvTypeReply(int xid, String ltag, int ecode, String buf) {
        int len = Const.header_len + strlen(ltag+ strlen(buf);
        Header(Const.SrvTypeRply, len, Const.normal_flag, xid);
        putString(ltag);                // language tag
        putShort(ecode);                // ErrCode
        putString(buf);                 // srvtype-list
        if (Const.MESSAGE_LOG_ENABLEDda.append("\nOUTGOING SRVTYPEREPLY MESSAGE"+
        "\n- Xid = "+ xid + 
        "\n- Ltag = "+ ltag +
        "\n- Error = " + ecode +
        "\n- Type List = " + buf + 
        "\n");
        return b.toByteArray();
    }

/**
 * DataRqst <#12> and DataRplyCmpl <#13> message
 *+-------------------------------+-------------------------------+
 *|      Anti-entropy type ID     |   Number of Accept ID entries |
 *+-------------------------------+-------------------------------+
 *|    Accept ID Entry 1         ...          Accept ID Entry k   \
 *+---------------------------------------------------------------+
 */
    public byte[] AntiEtrpRqst(int xid, String ltag, int type,
                               Vector adaList, Vector atsList) {
        int size = 0;
        for (int i=0; i<adaList.size(); i++) {
            size += 10 ((String)adaList.elementAt(i)).length();
        }
        int len = Const.header_len + strlen(ltag+ size;
        Header(Const.AntiEtrpRqst, len, Const.normal_flag, xid);
        putString(ltag);                // language tag
        putShort(type);
        putShort(adaList.size());
        for (int i=0; i<adaList.size(); i++) {
            putLong(((Long)atsList.elementAt(i)).longValue());
            putString((String)adaList.elementAt(i));
        }
        if (Const.MESSAGE_LOG_ENABLEDda.append("\nOUTGOING ANTIETRPRQST MESSAGE"+
        "\n- Xid = "+ xid + 
        "\n- Ltag = "+ ltag +
        "\n- Entropy Type = " + type + 
        "\n- ATS List = " + atsList.toString() 
        "\n- ADA List = " + adaList.toString() 
        "\n");
        return b.toByteArray();
    }

/**
 * append MeshFwd extension & adjust original message
 *+--------------------------------+----------------------------------+
 *|  MeshFwd Extension ID = 0x0006 |  Next Extension Offset (NEO)     |
 *+--------------+-----------------+----------------------------------+
 *|  NEO Contd.  |     Fwd-ID      |         Version Timestamp        |
 *+--------------+-----------------+----------------------------------+
 *|                     Version Timestamp, contd.                     |
 *+--------------------------------+----------------------------------+
 *|    Version Timestamp, contd.   |            Accept ID             \
 *+--------------------------------+----------------------------------+
 */
    public byte[] MeshFwdExt(byte[] buf, int id, long versionTS, 
                             String ada, long ats) {
        if (ada == null) {
            //da.appendDebug("Null acceptDA!");
            return buf;
        }
        int len = Util.parseInt(buf, 23);
        int alen = 14 10 + ada.length();
        adjustMesg(buf, alen);
        try {
            b.reset();
            d.write(buf, 0, len);
            d.writeShort(Const.MeshFwdExt);     // mesh-forwarding extension
            d.writeShort(0);                    // next ext. offset
            d.writeByte(0);                     // next ext. offset cont.
            d.writeByte(id);                    // Fwd-ID
            d.writeLong(versionTS);             // version timestamp
            d.writeLong(ats);                   // accept TS
            d.writeShort(ada.length());         // length of accept DA URL
            d.writeBytes(ada);                  // accept DA URL
        catch (Exception e) {
          da.append(e.toString());
        }
        return b.toByteArray();
    }

/**
 * append Select extension & adjust original message
 *+--------------------------------+----------------------------------+
 *|  Select Extension ID = 0x4002  |  Next Extension Offset (NEO)     |
 *+--------------+-----------------+----------------+-----------------+
 *|  NEO Contd.  |       Number of URL Entries      |
 *+--------------+----------------------------------+
 */
    public byte[] SelectExt(byte[] buf, int num) {
        int len = Util.parseInt(buf, 23);
        int alen = 7;
        adjustMesg(buf, alen);
        try {
            b.reset();
            d.write(buf, 0, len);
            d.writeShort(Const.SelectExt);      // selection extension
            d.writeShort(0);                    // next ext. offset
            d.writeByte(0);                     // next ext. offset cont.
            d.writeShort(num);                  // number of URL entries
        catch (Exception e) {
          da.append(e.toString());
        }
        return b.toByteArray();
    }

/**
 * append Sort extension & adjust original message
 *+--------------------------------+----------------------------------+
 *|   Sort Extension ID = 0x4003   |  Next Extension Offset (NEO)     |
 *+--------------+-----------------+---------------+------------------+
 *|  NEO Contd.  |      Length of sort key list    |   sort key list  \
 *+--------------+---------------------------------+------------------+
 */
    public byte[] SortExt(byte[] buf, String key) {
        int len = Util.parseInt(buf, 23);
        int alen = + key.length();
        adjustMesg(buf, alen);
        try {
            b.reset();
            d.write(buf, 0, len);
            d.writeShort(Const.SortExt);      // selection extension
            d.writeShort(0);                    // next ext. offset
            d.writeByte(0);                     // next ext. offset cont.
            d.writeShort(key.length());         // length of key
            d.writeBytes(key);                  // key string
        catch (Exception e) {
          da.append(e.toString());
        }
        return b.toByteArray();
    }

/**
 * AttrList extension
 */
    public byte[] AttrListExt(byte[] buf, Entry entry) {
        int len = Util.parseInt(buf, 23);
        int alen = 10;
        String url  = "";
        String attr = "";
        if (entry != null) {
            url  = entry.getURL();
            attr = entry.getAttr("");
            alen += url.length() + attr.length();
        }
        adjustMesg(buf, alen);
        try {
            b.reset();
            d.write(buf, 0, len);
            d.writeShort(Const.AttrListExt);    // AttrList extension
            d.writeShort(0);                    // next ext. offset
            d.writeByte(0);                     // next ext. offset cont.
            putString(url);
            putString(attr);
            d.writeByte(0);                     // num auths
        catch (Exception e) {
          da.append(e.toString());
        }
        return b.toByteArray();
    }
/**
 * adjust source message for the adding extension, need to change: 
 *   (1) packet length (add new length)
 *   (2) last extension's NEO links to new one
 */
    private void adjustMesg(byte[] buf, int alen) {
        int plen = Util.parseInt(buf, 23);
        int nextExt = Util.parseInt(buf, 73);
        int lastExtAddr = 7;
        while (nextExt != Const.EndOfExt) {
            lastExtAddr = nextExt+2;
            nextExt = Util.parseInt(buf, lastExtAddr, 3);
        }
        Util.writeInt(buf, lastExtAddr, plen, 3)// new ext. starting point
        Util.writeInt(buf, 2, plen+alen, 3);      // adjust message length
    }
}