Java Source Code: com.verisignlabs.pimmit.iris.LwzTransportBinding


   1: /*
   2: Copyright (C) 2005  VeriSign, Inc. by Andrew Newton
   3: 
   4: This library is free software; you can redistribute it and/or
   5: modify it under the terms of the GNU Lesser General Public
   6: License as published by the Free Software Foundation; either
   7: version 2.1 of the License, or (at your option) any later version.
   8: 
   9: This library is distributed in the hope that it will be useful,
  10: but WITHOUT ANY WARRANTY; without even the implied warranty of
  11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12: Lesser General Public License for more details.
  13: 
  14: You should have received a copy of the GNU Lesser General Public
  15: License along with this library; if not, write to the Free Software
  16: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  17: */
  18: package com.verisignlabs.pimmit.iris;
  19: 
  20: import java.io.ByteArrayInputStream;
  21: import java.io.ByteArrayOutputStream;
  22: import java.io.DataOutputStream;
  23: import java.io.IOException;
  24: import java.io.InputStream;
  25: import java.net.DatagramPacket;
  26: import java.net.DatagramSocket;
  27: import java.util.zip.DataFormatException;
  28: import java.util.zip.Deflater;
  29: import java.util.zip.Inflater;
  30: 
  31: import com.verisignlabs.jsnatpr.JSNaptr.EndPoint;
  32: 
  33: 
  34: /**
  35:  * Provides a transport binding for LWZ.
  36:  * @author anewton
  37:  */
  38: public class LwzTransportBinding implements TransportBinding
  39:	  {
  40:
  41:  public static String APP_TRANS_LABEL   = "iris.lwz";
  42:  
  43:  //TODO make this configurable
  44:  public static int MAX_UDP_LENGTH    = 492;
  45:  public static int UDP_HEADER_LENGTH = 8;
  46:
  47:  private String[]         profiles;
  48:  private String           authority;
  49:  private boolean          connected;
  50:  private EndPoint         endPoint;
  51:  
  52:  public LwzTransportBinding()
  53:	    {
  54:    super();
  55:    profiles = new String[ 0 ];
  56:  }
  57:
  58:  /* (non-Javadoc)
  59:   * @see com.verisignlabs.pimmit.iris.TransportBinding#isConnected()
  60:   */
  61:  public boolean isConnected()
  62:	    {
  63:    return connected;
  64:  }
  65:
  66:  /* (non-Javadoc)
  67:   * @see com.verisignlabs.pimmit.iris.TransportBinding#connect( EndPoint endPoint, String authority )
  68:   */
  69:  public void connect( EndPoint endPoint, String authority ) throws ConnectionException
  70:	    {
  71:    //since there is no real concept of sessions for UDP, then there really isn't much to do here.
  72:    this.authority = authority;
  73:    this.endPoint  = endPoint;
  74:    connected = true;
  75:    //TODO if the spec gets updated, ping the server for version info and place it in profiles.
  76:  }
  77:
  78:  /* (non-Javadoc)
  79:   * @see com.verisignlabs.pimmit.iris.TransportBinding#disconnect()
  80:   */
  81:  public void disconnect() throws DisconnectionException
  82:	    {
  83:    connected = false;
  84:  }
  85:
  86:  /* (non-Javadoc)
  87:   * @see com.verisignlabs.pimmit.iris.TransportBinding#channelTest( String profileId )
  88:   */
  89:  public void channelTest( String registryType ) throws ChannelTestException, InvalidRegistryTypeException
  90:	    {
  91:    //TODO send test packet.  this requires a change to the draft. apparently there is no legal way to ping
  92:  }
  93:
  94:  /* (non-Javadoc)
  95:   * @see com.verisignlabs.pimmit.iris.TransportBinding#queryContext()
  96:   */
  97:  public QueryContext queryContext( String registryType )
  98:    throws QueryException, InvalidRegistryTypeException
  99:	    {
 100:    LwzQueryContext lqctx = new LwzQueryContext();
 101:    return lqctx;
 102:  }
 103:
 104:  /* (non-Javadoc)
 105:   * @see com.verisignlabs.pimmit.iris.TransportBinding#authenticate(com.verisignlabs.pimmit.iris.AuthenticationAction, 
 106:   *   String Authority)
 107:   */
 108:  public void authenticate( AuthenticationAction action, String authority )
 109:      throws AuthenticateException
 110:	    {
 111:    //nothing to do
 112:  }
 113:  
 114:  /**
 115:   * @return Returns the plainPasswordAvailable.
 116:   */
 117:  public boolean isPlainPasswordAvailable()
 118:	    {
 119:    return false;
 120:  }
 121:  /**
 122:   * @return Returns the tlsAvailable.
 123:   */
 124:  public boolean isTlsAvailable()
 125:	    {
 126:    return false;
 127:  }
 128:  /**
 129:   * @return Returns the tlsRequired.
 130:   */
 131:  public boolean isTlsRequired()
 132:	    {
 133:    return false;
 134:  }
 135:  
 136:  public String[] getProfiles()
 137:	    {
 138:    return profiles;
 139:  }
 140:
 141:  public class LwzQueryContext extends QueryContext
 142:	    {
 143:    private String profileUri;
 144:    
 145:    public LwzQueryContext()
 146:	      {
 147:      request = new ByteArrayOutputStream();
 148:    }
 149:    
 150:    public synchronized InputStream submitQuery()
 151:    throws QueryException
 152:	      {
 153:      InputStream is = null;
 154:      try
 155:	        {
 156:        request.close();
 157:        byte[] authBytes = authority.getBytes();
 158:        boolean deflated = false;
 159:        byte[] data = ((ByteArrayOutputStream)request).toByteArray();
 160:        if( data.length + UDP_HEADER_LENGTH + 1 + authBytes.length > MAX_UDP_LENGTH )
 161:	          {
 162:          deflated = true;
 163:          data = deflate( data );
 164:        }
 165:        
 166:        int transactionId = (int)(Math.random() * Short.MAX_VALUE);
 167:
 168:        //send the message
 169:        ByteArrayOutputStream output = new ByteArrayOutputStream();
 170:        DataOutputStream dataOut = new DataOutputStream( output );
 171:        byte header = 0x10;  //version 1, deflate supported, request packet, no errors
 172:        if( deflated )
 173:	          {
 174:          header = (byte)(header | 0x20);
 175:        }
 176:        dataOut.writeByte( header );
 177:        dataOut.writeShort( transactionId );
 178:        dataOut.writeShort( MAX_UDP_LENGTH );
 179:        dataOut.writeByte( authBytes.length );
 180:        dataOut.write( authBytes );
 181:        dataOut.write( data );
 182:        dataOut.close();
 183:        output.close();
 184:        DatagramPacket response = sendExchange( output.toByteArray() );
 185:        data = processLwzResponse( response, transactionId );
 186:        is = new ByteArrayInputStream( data );
 187:      }
 188:      catch( IOException e )
 189:	        {
 190:        throw new QueryException( e );
 191:      }
 192:      catch( DataFormatException e )
 193:	        {
 194:        throw new QueryException( e );
 195:      }
 196:      return is;
 197:    }
 198:  }
 199:
 200:  private byte[] deflate( byte[] data )
 201:  throws DataFormatException
 202:	    {
 203:    Deflater deflater = new Deflater();
 204:    deflater.setInput( data );
 205:    deflater.finish();
 206:    byte[] b = new byte[ data.length ];
 207:    int length = deflater.deflate( b );
 208:    byte[] retval = new byte[ length ];
 209:    System.arraycopy( b, 0, retval, 0, length );
 210:    return retval;
 211:  }
 212:  
 213:  private byte[] inflate( byte[] data, int offset, int length )
 214:  throws DataFormatException
 215:	    {
 216:    Inflater inflater = new Inflater();
 217:    inflater.setInput( data, offset, length );
 218:    byte[] result = new byte[ data.length * 4 ];  //should be enough room
 219:    int resultLength = inflater.inflate( result );
 220:    inflater.end();
 221:    byte[] retval = new byte[ resultLength ];
 222:    System.arraycopy( result, 0, retval, 0, resultLength ); 
 223:    return retval;
 224:  }
 225:  
 226:  private DatagramPacket sendExchange( byte[] data )
 227:  throws IOException
 228:	    {
 229:    DatagramSocket socket = new DatagramSocket();
 230:    socket.setSoTimeout( 5000 );
 231:    DatagramPacket packet = new DatagramPacket( data, 0, data.length, 
 232:        endPoint.getInetAddress(), endPoint.getPort() );
 233:    socket.send( packet );
 234:    DatagramPacket responsePacket = new DatagramPacket( new byte[ MAX_UDP_LENGTH ], MAX_UDP_LENGTH );
 235:    socket.receive( responsePacket );
 236:    socket.close();
 237:    return responsePacket;
 238:  }
 239:  
 240:  private byte[] processLwzResponse( DatagramPacket response, int transactionId )
 241:  throws QueryException, DataFormatException
 242:	    {
 243:    //get the reply
 244:    byte[] data = response.getData();
 245:    int dataLength = response.getLength();
 246:    int dataOffset = response.getOffset();
 247:    if( data == null || dataLength == 0 )
 248:	      {
 249:      throw new QueryException( Util.getCoreString( "E_082" ) );
 250:    }
 251:    int rtId = (((data[ 1 ] & 0xff) << 8) | (data[ 2 ] & 0xff));
 252:    if( rtId != transactionId )
 253:	      {
 254:      throw new QueryException( Util.getCoreString( "E_082" ) );
 255:    }
 256:    boolean responseDeflated = (data[ 0 ] & 0x20) == 0 ? false : true;
 257:    if( ( data[ 0 ] & 0x40 ) == 0 )
 258:	      {
 259:      throw new QueryException( Util.getCoreString( "E_082" ) );
 260:    }
 261:    int error = data[ 0 ] & 0x03;
 262:    switch( error )
 263:	      {
 264:      case 1:
 265:        QueryException qe = new QueryException( Util.getCoreString( "E_082" ) );
 266:        qe.setVersionMismatch( true );
 267:        throw qe;
 268:      case 2:
 269:        QueryException qe2 = new QueryException( Util.getCoreString( "E_082" ) );
 270:        qe2.setSizeError( true );
 271:        throw qe2;
 272:      case 3:
 273:        QueryException qe3 = new QueryException( Util.getCoreString( "E_082" ) );
 274:        throw qe3;
 275:      default:
 276:        //do nothing
 277:    }
 278:    
 279:    byte[] responsePayload = null;
 280:    if( responseDeflated )
 281:	      {
 282:      responsePayload = inflate( data, dataOffset + 3, dataLength - 3 );
 283:    }
 284:    else
 285:	      {
 286:      responsePayload = new byte[ dataLength - 3 ];
 287:      System.arraycopy( data, dataOffset + 3, responsePayload, 0, dataLength - 3 );
 288:    }
 289:    return responsePayload;
 290:  }
 291:}