Home | History | Annotate | Download | only in jist
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  *
     26  * ident	"@(#)JISTAgent.java	1.25	08/12/04 SMI"
     27  */
     28 
     29 package com.sun.jist;
     30 import com.sun.jist.JISTData;
     31 import com.sun.jist.JISTLogic;
     32 import com.sun.jist.iscsi.iSCSIData;
     33 import java.io.BufferedWriter;
     34 import java.io.IOException;
     35 import java.io.OutputStreamWriter;
     36 import java.io.Writer;
     37 import java.net.InetAddress;
     38 import java.net.ServerSocket;
     39 import java.net.Socket;
     40 import java.net.SocketException;
     41 import java.net.SocketTimeoutException;
     42 import java.net.UnknownHostException;
     43 
     44 /**
     45  * JIST Proxy Agent used to intercept communications for Queries,
     46  * Additions, Changes, and/or Deletions.
     47  * <p>
     48  * Verifies Compliance to IETF iSCSI and allows for SCSI fault
     49  * injection.
     50  * <p>
     51  * @author	Joel.Buckley (at) Sun.COM
     52  * @since	5.0
     53  */
     54 public class JISTAgent extends JISTLogic {
     55 
     56 /** Declare & Initialize "public final static" Constants. */
     57 
     58 /**
     59  * Spin cycle in Milliseconds.
     60  * <p>
     61  * This is used for Read & Write Threads to avoid hangs.
     62  */
     63 private final static long SPIN = 50L;
     64 
     65 /** Data IN "READ" Direction. */
     66 private final static byte DATA_IN = 0;
     67 
     68 /** Data OUT "WRITE" Direction. */
     69 private final static byte DATA_OUT = 1;
     70 
     71 /**
     72  * JIST iSCSI Proxy Target Server Socket IP Address.
     73  * <p>
     74  * This is set by Java Environment Property "proxyIP=localhost".  By default,
     75  * this is set to localhost or 127.0.0.1.
     76  */
     77 public final static String proxyIP;
     78 
     79 /** Proxy Port Mutex. */
     80 private final static Object proxyPortMutex = new Object();
     81 
     82 /** Declare "static" Class Variables. */
     83 
     84 /**
     85  * JIST iSCSI Proxy Management Server Socket Port.
     86  * <p>
     87  * This is set by Java Environment Property "mgntPort=ffffffff". By
     88  * default this is set to -1 and must be specified.
     89  */
     90 public static volatile int mgntPort;
     91 
     92 /**
     93  * JISTAgent Thread number, per JVM Counter.
     94  */
     95 private static volatile int threadID;
     96 
     97 /**
     98  * JIST iSCSI Proxy Target Server Socket Port.
     99  * <p>
    100  * This is set by Java Environment Property "proxyPort=ffffffff". By
    101  * default this is set to -1 and must be specified.
    102  */
    103 public static volatile int[] proxyPort;
    104 
    105 /**
    106  * Actual iSCSI Target Server Socket Port.
    107  * <p>
    108  * This is set by Java Environment Property "targetPort=cbc".  By
    109  * default, this is set to 3260 or 0x0cbc.
    110  */
    111 public static volatile int[] targetPort;
    112 
    113 /**
    114  * Actual iSCSI Target Server Socket IP Address.
    115  * <p>
    116  * This is set by Java Environment Property "targetIP=localhost".  By
    117  * default, this is set to localhost or 127.0.0.1.
    118  */
    119 public static volatile String[] targetIP;
    120 
    121 /** Declare non-"static" Instance Variables. */
    122 
    123 /**
    124  * JISTAgent Thread number, instance specific.
    125  */
    126 private volatile String myID;
    127 
    128 /** Declare and Initialize non-"static" Instance Constants. */
    129 
    130 /**
    131  * JISTAgent ServerSocket Thread.
    132  */
    133 private final boolean serverSocket;
    134 
    135 /**
    136  * JISTAgent Read or Write Thread.
    137  */
    138 private final boolean readAgent;
    139 
    140 /**
    141  * JIST iSCSI Proxy Target Server Index.
    142  * <p>
    143  * This value is used associate a ProxyPort with a TargetIP/TargetPort
    144  * combination.  This is necessary for iSCSI Target Port Redirection.
    145  */
    146 private final int proxyIndex;
    147 
    148 /**
    149  * JISTAgent Master Thread.
    150  */
    151 private final JISTAgent master;
    152 
    153 /** Initialize "static" Class Variables. */
    154 static {
    155 	/** Booleans. */
    156 
    157         /** I/O Paths. */
    158 
    159         /** Test Settings. */
    160 	mgntPort = getIntProperty("mgntPort", -1);
    161 	proxyPort = new int[1];
    162 	proxyPort[0] = getIntProperty("proxyPort", -1);
    163 	targetPort = new int[1];
    164 	targetPort[0] = getIntProperty("targetPort", 3260);
    165 	String ip;
    166 	try {
    167 		ip = InetAddress.getLocalHost().getHostAddress();
    168 		ip = InetAddress.getByName(getStringProperty("proxyIP", ip))
    169 		    .getHostAddress();
    170 	} catch (UnknownHostException e) {
    171 		ip = "127.0.0.1";
    172 	}
    173 	proxyIP = ip;
    174 	targetIP = new String[1];
    175 	try {
    176 		ip = InetAddress.getLocalHost().getHostAddress();
    177 		ip = InetAddress.getByName(getStringProperty("targetIP", ip))
    178 		    .getHostAddress();
    179 	} catch (UnknownHostException e) {
    180 		ip = "127.0.0.1";
    181 	}
    182 	targetIP[0] = ip;
    183 
    184         /** Internal Variables - long. */
    185 
    186         /** Internal Variables - int. */
    187 	threadID = 0;
    188 
    189         /** Internal Variables - short. */
    190 
    191         /** Internal Variables - byte. */
    192 
    193         /** Internal Variables - Arrays. */
    194 
    195         /** Internal Variables - String. */
    196 
    197         /** Internal Variables - misc. */
    198 }
    199 
    200 /** Initialize non-"static" Instance Variables. */
    201 {
    202 	/** Booleans. */
    203 
    204         /** I/O Paths. */
    205 
    206         /** Test Settings. */
    207 
    208         /** Internal Variables - long. */
    209 
    210         /** Internal Variables - int. */
    211 
    212         /** Internal Variables - short. */
    213 
    214         /** Internal Variables - byte. */
    215 
    216         /** Internal Variables - Arrays. */
    217 
    218         /** Internal Variables - String. */
    219 	myID = "JISTAgent" + (++threadID);
    220 
    221         /** Internal Variables - misc. */
    222 }
    223 
    224 /**
    225  * First constructor for JIST Agent ServerSocket use.
    226  * <p>
    227  * Specifing a negative value for proxyIndex starts the JIST Proxy Management
    228  * Server.
    229  * <p>
    230  * Specifing a positive value for proxyIndex starts a JIST Proxy iSCSI Target
    231  * Server.
    232  * <p>
    233  * @param proxyIndex JIST Proxy iSCSI Target Server Index.
    234  */
    235 public JISTAgent(int proxyIndex) {
    236 	super();
    237 	master = this;
    238 	tester = this;
    239 	serverSocket = true;
    240 	readAgent = true;
    241 	this.dataAgent = (proxyIndex >= 0);
    242 	this.proxyIndex = proxyIndex;
    243 }
    244 
    245 /**
    246  * Second constructor to establish a new JIST Agent socket.
    247  * <p>
    248  * @param socket JISTAgent external Socket socket.
    249  * @param proxyIndex JIST Proxy iSCSI Target Server Index.
    250  */
    251 private JISTAgent(Socket socket, int proxyIndex) {
    252 	super();
    253 	master = this;
    254 	tester = this;
    255 	serverSocket = false;
    256 	readAgent = true;
    257 	this.socket = socket;
    258 	this.dataAgent = (proxyIndex >= 0);
    259 	this.targetAgent = true;
    260 	this.proxyIndex = proxyIndex;
    261 
    262 	if (dataAgent) {
    263 		if (mgntPort == -1) {
    264 			// Create Initiator Read Thread
    265 			this.peer = new JISTAgent(this, proxyIndex, dataAgent,
    266 			    !targetAgent);
    267 		} else {
    268 			// Create Management Write Thread
    269 			this.peer = new JISTAgent(this, proxyIndex, !dataAgent,
    270 			    targetAgent);
    271 		}
    272 		Thread t = new Thread(peer);
    273 		t.setPriority(Thread.NORM_PRIORITY + 1);
    274 		t.start();
    275 	}
    276 }
    277 
    278 /**
    279  * Third constructor to link JIST Initiator, Delta, & Target Agents.
    280  * <p>
    281  * In this constructor, the peer passes a reference to itself
    282  * such that bidirectional communications can be establish
    283  * between the JISTAgent instances.
    284  * <p>
    285  * @param peer JISTAgent Peer Thread asking for the object.
    286  * @param proxyIndex JIST Proxy iSCSI Target Server Index.
    287  * @param targetAgent JISTAgent Target or Initiator Thread flag.
    288  */
    289 private JISTAgent(JISTAgent peer, int proxyIndex, boolean dataAgent,
    290     boolean targetAgent) {
    291 	super();
    292 	master = this;
    293 	tester = this;
    294 	serverSocket = false;
    295 	this.peer = peer;
    296 	this.proxyIndex = proxyIndex;
    297 	this.dataAgent = dataAgent;
    298 	this.targetAgent = targetAgent;
    299 	dupBufferedWriter(peer);
    300 
    301 	/*
    302 	 * Data Agents default to readAgent as primary thread.
    303 	 * Management Agents default to write agent as primary thread.
    304 	 */
    305 	this.readAgent = dataAgent;
    306 
    307 	if (mgntPort != -1 && !dataAgent) {
    308 		// Create Initiator Read Thread
    309 		this.peer2 =
    310 		    new JISTAgent(this, proxyIndex, !dataAgent, !targetAgent);
    311 		Thread t = new Thread(peer2);
    312 		t.setPriority(Thread.NORM_PRIORITY + 1);
    313 		t.start();
    314 	}
    315 }
    316 
    317 /**
    318  * Forth constructor to Start Read & Write Threads.
    319  * <p>
    320  * In this constructor, the peer passes a reference to itself
    321  * such that bidirectional communications can be establish
    322  * between the JISTAgent instances.
    323  * <p>
    324  * @param master JISTAgent Master Thread asking for the object.
    325  * @param dataAgent JISTAgent Data or Management Thread flag.
    326  * @param targetAgent JISTAgent Target or Initiator Thread flag.
    327  * @param readAgent JISTAgent Read or Write Thread flag.
    328  */
    329 private JISTAgent(JISTAgent master, boolean dataAgent, boolean targetAgent,
    330     boolean readAgent) {
    331 	super();
    332 	serverSocket = false;
    333 	this.master = master;
    334 	this.tester = master;
    335 	this.proxyIndex = master.proxyIndex;
    336 	this.dataAgent = dataAgent;
    337 	this.targetAgent = targetAgent;
    338 	this.readAgent = readAgent;
    339 	this.peer = master.peer;
    340 	dupBufferedWriter(tester);
    341 }
    342 
    343 /**
    344  * Main entry point.
    345  */
    346 public static void main(String[] args) {
    347 	Thread t = new Thread(new JISTAgent(0));	// Proxy
    348 	t.setPriority(Thread.NORM_PRIORITY + 1);
    349 	t.start();
    350 	new Thread(new JISTAgent(-1)).start();	// Management
    351 	/* The above threads will prevent the object from exiting... */
    352 }
    353 
    354 /**
    355  * Main execution engines for listening ServerSocket, individual
    356  * socket reader, and individual socket writers.
    357  */
    358 public void run() {
    359 	/** Verify new Thread full name. */
    360 	if (reportLevel > 8) {
    361 		myID = new StringBuilder(myID)
    362 		    .append(dataAgent ? ":\"Data " : ":\"Management ")
    363 		    .append(targetAgent ? "Target " : "Initiator ")
    364 		    .append(readAgent ? "Read " : "Write ")
    365 		    .append(serverSocket ? "Server\"" : "Connection\"")
    366 		    .toString();
    367 	}
    368 	Thread.currentThread().setName(myID);
    369 
    370 	log(MILESTONE, "JISTAgent Thread Start", myID);
    371 	if (serverSocket) {
    372 		// Handle ServerSockets here.
    373 		listen();
    374 	} else if (!dataAgent && readAgent) {
    375 		// Handle Management Connections.
    376 		interact();
    377 	} else if (readAgent) {
    378 		// Start Write Connection Thread.
    379 		Thread t = new Thread(new JISTAgent(
    380 		    this, dataAgent, targetAgent, false));
    381 		t.setPriority(Thread.NORM_PRIORITY + 1);
    382 		t.start();
    383 
    384 		// Data Read Connection Threads.
    385 		if (master.openByteSocket(master, targetIP[proxyIndex],
    386 		    targetPort[proxyIndex])) {
    387 			JISTData obj;
    388 			while ((obj = new iSCSIData()) != null &&
    389 			    master.read(obj, 3600) && peer.writeQueue(obj));
    390 		}
    391 		master.deStress(master);
    392 		master.close(master);
    393 	} else {
    394 		// Data Write Connection Threads.
    395 		if (master.openByteSocket(master, targetIP[proxyIndex],
    396 		    targetPort[proxyIndex])) {
    397 			/** Core loop for Write Threads. No Printing Here. */
    398 			while (running && tester.stressing &&
    399 			    master.writeImmediate(
    400 			    ((JISTData)master.dequeue())));
    401 		}
    402 		master.deStress(master);
    403 		master.close(master);
    404 	}
    405 	log(MILESTONE, "JISTAgent Thread Complete", myID);
    406 }
    407 
    408 /**
    409  * Set Stressing to false to speed data socket thread shutdown.
    410  * <p>
    411  * @param customer Who is asking for the service.
    412  */
    413 private final void deStress(JISTAgent customer) {
    414 	tester.stressing = false;
    415 	if (null != peer2 && peer2 != customer) {
    416 		peer2.deStress(customer);
    417 	}
    418 	if (null != peer && peer != customer) {
    419 		peer.deStress(customer);
    420 	}
    421 }
    422 
    423 /**
    424  * Listen for incoming socket connections.
    425  * <p>
    426  * Note: All output goes to the console.
    427  */
    428 private final void listen() {
    429 	int localPort, remotePort;
    430 	String remoteIP;
    431 	ServerSocket ss = null;
    432 	Thread t = null;
    433 
    434 	if (proxyIndex < 0) {
    435 		log(MILESTONE, "Security Notice", getMsg(107));
    436 	}
    437 	synchronized (proxyPortMutex) {
    438 		if (!dataAgent) {
    439 			localPort = proxyPort[0];
    440 			remotePort = -1;
    441 		} else if (proxyIndex < 0 || proxyIndex >= proxyPort.length) {
    442 			localPort = -1;
    443 			remotePort = -1;
    444 		} else {
    445 			localPort = proxyPort[proxyIndex];
    446 			remotePort = targetPort[proxyIndex];
    447 		}
    448 	}
    449 
    450 	if (!dataAgent && mgntPort < 0) {
    451 		/** No Delta Agent.  Nothing else to do. */
    452 		return;
    453 	} else if (dataAgent && localPort < 0) {
    454 		/** No Proxy Agent.  Nothing else to do. */
    455 		return;
    456 	} else if (mgntPort == localPort) {
    457 		log(WARNING,
    458 		    "JIST iSCSI Proxy Management & Target ports conflict",
    459 		    getMsg(902));
    460 		localPort = -1;
    461 	} else if (dataAgent && localPort == remotePort) {
    462 		synchronized (proxyPortMutex) {
    463 			remoteIP = targetIP[proxyIndex];
    464 		}
    465 		try {
    466 			if (InetAddress.getByName(remoteIP)
    467 			    .isAnyLocalAddress()) {
    468 				log(WARNING,
    469 				    "JIST Proxy & Target Ports conflict.",
    470 				    getMsg(902));
    471 				localPort = -1;
    472 			}
    473 		} catch (UnknownHostException uhe) {
    474 			log(WARNING, "iSCSI Target IP Address undefined",
    475 			    getMsg(902));
    476 			localPort = -1;
    477 		}
    478 	}
    479 
    480 	/** Unlock thread that created this thread. */
    481 	if (dataAgent && localPort < 0) {
    482 		synchronized (proxyPortMutex) {
    483 			proxyPort[proxyIndex] = localPort;
    484 			proxyPortMutex.notifyAll();
    485 		}
    486 		return;
    487 	}
    488 
    489 	try {
    490 		if (dataAgent) {
    491 			ss = new ServerSocket(localPort);
    492 			localPort = ss.getLocalPort();
    493 			synchronized (proxyPortMutex) {
    494 				proxyPort[proxyIndex] = localPort;
    495 				proxyPortMutex.notifyAll();
    496 			}
    497 		} else {
    498 			ss = new ServerSocket(mgntPort);
    499 		}
    500 		ss.setSoTimeout(1000); // 1 second timeout.
    501 		while (running) {
    502 			try {
    503 				// New Target or Management Read Thread
    504 				t = new Thread(new JISTAgent(ss.accept(),
    505 				    proxyIndex));
    506 				t.setPriority(Thread.NORM_PRIORITY + 1);
    507 				t.start();
    508 			} catch (SocketTimeoutException e) {
    509 				// keep checking running variable...
    510 			}
    511 		}
    512 	} catch (IOException e) {
    513 		log(WARNING, "JISTAgent Server Socket, Unexpected Error", e);
    514 	} finally {
    515 		try {
    516 			if (ss != null) {
    517 				ss.close();
    518 			}
    519 		} catch (IOException e2) {
    520 			log(DEBUG, "Close Server Socket Exception", e2);
    521 		}
    522 	}
    523 }
    524 
    525 /**
    526  * Interact with management socket connections.
    527  * <p>
    528  * Note:jj
    529  */
    530 private final void interact() {
    531 	final String complete = getMsg(196) + newline;
    532 	final String prompt = getMsg(410);
    533 	final String who = new StringBuilder(myID).append(" from ")
    534 	    .append(socket.getInetAddress().getCanonicalHostName()).toString();
    535 	String command;
    536 	if (openCharSocket(this, "", -1)) {
    537 		log(MILESTONE, "JISTAgent Thread Interacting", who);
    538 		while (running) {
    539 			if (!write(prompt)) {
    540 				log(WARNING, "JISTAgent Thread Socket Closed",
    541 				    who);
    542 				break;
    543 			}
    544 			if ((command = readLine()) == null) {
    545 				log(WARNING, "JISTAgent Thread Input Closed",
    546 				    who);
    547 				break;
    548 			}
    549 			if (command.equals("") || command.matches("\\s*")) {
    550 				continue;
    551 			}
    552 			if (command.equals("exitall")) {
    553 				log(MILESTONE, "JISTAgent Thread Exit All",
    554 				    who);
    555 				running = false;
    556 				close(this);
    557 				break;
    558 			}
    559 			if (command.equals("exit")) {
    560 				log(MILESTONE, "JISTAgent Thread Exit", who);
    561 				close(this);
    562 				break;
    563 			}
    564 			if (!running) {
    565 				log(MILESTONE, "JISTAgent Thread Not Running",
    566 				    who);
    567 				close(this);
    568 				break;
    569 			}
    570 			if (command.startsWith("inject")) {
    571 				String[] args = command.split("\\s");
    572 				if (args.length > 1) {
    573 					while (!JISTData.addMask(args[1])) {
    574 						write(getMsg(411));
    575 					}
    576 				}
    577 			} else if (command.startsWith("com.sun.jist")) {
    578 				execute(command);
    579 			} else if (!write(new StringBuilder(
    580 			    "Unrecognized request \"").append(command)
    581 			    .append("\", skipping.").toString())) {
    582 				break;
    583 			}
    584 			if (!write(complete)) {
    585 				log(WARNING, "JISTAgent Thread Output Closed",
    586 				    who);
    587 				break;
    588 			}
    589 		}
    590 	} else {
    591 		log(WARNING, "JISTAgent Thread Socket DOA", who);
    592 	}
    593 }
    594 
    595 /**
    596  * Write JISTData Object to Byte Stream Output.
    597  * <p>
    598  * @param data JIST Data Object to be written.
    599  */
    600 private final boolean writeQueue(JISTData data) {
    601 	if (data == null) {
    602 		// Read failure... initiate closing process.
    603 		log(DEBUG, "JISTAgent Write Data Null",
    604 		    new StringBuilder(myID).append(" Called By ")
    605 		    .append(Thread.currentThread().getName()).toString());
    606 		return false;
    607 	}
    608 	if (data.getImmediate() == 0 && master.enqueue(data)) {
    609 		// Regular Command and available Queue.
    610 		return true;
    611 	} else {
    612 		// PDU Immediate bit set or uninitialized Queue.
    613 		return writeImmediate(data);
    614 	}
    615 }
    616 
    617 /**
    618  * Internal write method behind write queue.
    619  * <p>
    620  * @param data JIST Data Object to be written.
    621  */
    622 private final boolean writeImmediate(JISTData data) {
    623 	if (data == null) {
    624 		// Read failure... initiate closing process.
    625 		log(DEBUG, "JISTAgent Write Data Null",
    626 		    new StringBuilder(myID).append(" Called By ")
    627 		    .append(Thread.currentThread().getName()).toString());
    628 		return false;
    629 	}
    630 	if (dataAgent) {
    631 		if (master.write(data) &&
    632 		    data.getOpcode() != 0x026) {
    633 			return true;
    634 		} else {
    635 			/**
    636 			 * Initiate closing process on failed write or
    637 			 * iSCSI Logout Request PDU.
    638 			 */
    639 			return false;
    640 		}
    641 	} else {
    642 		// Data Manipulation done here...
    643 		if (reportLevel > 8) {
    644 			log(DEBUG, "JISTAgent iSCSI PDU Header, before Mask",
    645 			    data.getHeader());
    646 			log(DEBUG, "JISTAgent iSCSI PDU Payload, before Mask",
    647 			    data.getPayload());
    648 			switch (data.getOpcode()) {
    649 			case(0x003):
    650 			case(0x004):
    651 			case(0x023):
    652 			case(0x024):
    653 				log(DEBUG, "JISTAgent iSCSI PDU " +
    654 				    "Payload, before Mask, in ASCII",
    655 				    new String(data.getPayload(), 0,
    656 				    data.getPayload().length)
    657 				    .replaceAll("\00", newline));
    658 				break;
    659 			}
    660 		}
    661 
    662 
    663 		JISTData atad; // Data backwards :)
    664 		JISTData[] masked = data.mask();
    665 		if (masked == null) {
    666 			/** Attempting to Disconnect... Aburptly. */
    667 			return false;
    668 		}
    669 		int index = masked.length;
    670 
    671 		while (index > 0) {
    672 			atad = masked[--index];
    673 
    674 			if (atad == null) {
    675 				continue;
    676 			}
    677 
    678 			if (reportLevel > 8) {
    679 				log(DEBUG,
    680 				    "JISTAgent iSCSI PDU Header, after Mask",
    681 				    atad.getHeader());
    682 				log(DEBUG,
    683 				    "JISTAgent iSCSI PDU Payload, after Mask",
    684 				    atad.getPayload());
    685 				switch (data.getOpcode()) {
    686 				case(0x003):
    687 				case(0x004):
    688 				case(0x023):
    689 				case(0x024):
    690 					log(DEBUG, "JISTAgent iSCSI PDU " +
    691 					    "Payload, after Mask, in ASCII",
    692 					    new String(atad.getPayload(), 0,
    693 					    atad.getPayload().length)
    694 					    .replaceAll("\00", newline));
    695 					break;
    696 				}
    697 			}
    698 
    699 			if (atad.getDirection() == DATA_IN) {
    700 				if (!peer.writeQueue(atad)) {
    701 					return false;
    702 				}
    703 			} else {
    704 				if (!peer2.writeQueue(atad)) {
    705 					return false;
    706 				}
    707 			}
    708 		}
    709 
    710 		return true;
    711 	}
    712 }
    713 
    714 /**
    715  * Common method to lookup and, if necessary, increment the number of JIST
    716  * iSCSI Proxy Target Servers.
    717  * <p>
    718  * @param ip iSCSI Target IP Address.
    719  * @param port iSCSI Target Port Number.
    720  * @return proxyIndex Position where IP/Port combination was placed.
    721  */
    722 public static int getProxyIndex(String ip, int port) {
    723 	int inx;
    724 	int[] proxyPortNew, targetPortNew;
    725 	String[] targetIPNew;
    726 	Thread t;
    727 
    728 	synchronized (proxyPortMutex) {
    729 		inx = targetIP.length;
    730 
    731 		/** Is the IP:Port combination served by a Proxy? */
    732 		while (--inx >= 0) {
    733 			if (targetPort[inx] == port &&
    734 			    targetIP[inx].equals(ip)) {
    735 				return (inx);
    736 			}
    737 		}
    738 
    739 		/** Well, Proxy not found... Create one... */
    740 		inx = targetIP.length;
    741 
    742 		proxyPortNew = new int[inx + 1];
    743 		System.arraycopy(proxyPort, 0, proxyPortNew, 0,
    744 		    proxyPort.length);
    745 		proxyPortNew[inx] = 0; /** Temp. until Thread started below. */
    746 		proxyPort = proxyPortNew;
    747 
    748 		targetPortNew = new int[inx + 1];
    749 		System.arraycopy(targetPort, 0, targetPortNew, 0,
    750 		    targetPort.length);
    751 		targetPortNew[inx] = port;
    752 		targetPort = targetPortNew;
    753 
    754 		targetIPNew = new String[inx + 1];
    755 		System.arraycopy(targetIP, 0, targetIPNew, 0, targetIP.length);
    756 		targetIPNew[inx] = ip;
    757 		targetIP = targetIPNew;
    758 
    759 		/** Start the JIST iSCSI Proxy Target Server. */
    760 		t = new Thread(new JISTAgent(inx));	// Proxy
    761 		t.setPriority(Thread.NORM_PRIORITY + 1);
    762 	}
    763 
    764 	/** Start the Thread outside the Synchronized Block. */
    765 	t.start();
    766 
    767 	synchronized (proxyPortMutex) {
    768 		/** Wait for the real proxyPort to be assigned. */
    769 		boolean i = false;
    770 		while (proxyPort[inx] == 0) {
    771 			try {
    772 				proxyPortMutex.wait(SPIN);
    773 			} catch (InterruptedException e) {
    774 				i = true;
    775 			}
    776 		}
    777 		if (i) {
    778 			Thread.currentThread().interrupt();
    779 		}
    780 	}
    781 
    782 	return (inx);
    783 }
    784 
    785 }  /* Class End */
    786