001: package test;
002:
003: import java.io.*;
004: import java.net.*;
005:
006: /**
007: Server to used perform tests for SOCKS library.
008: */
009: public class TestService implements Runnable {
010: static final String chargenSequence = " !\"#$%&'()*+,-./0123456789:;<=>?@"
011: + "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefg";
012:
013: static final String serviceNames[] = { "echo", "discard",
014: "chargen", "connect" };
015: static final int servicePorts[] = { 5678, 5679, 5680, 5681 };
016:
017: static final int ECHO = 0;
018: static final int DISCARD = 1;
019: static final int CHARGEN = 2;
020: static final int CONNECT = 3;
021:
022: static final int BUF_SIZE = 1024;
023:
024: static final int CHARGEN_WAIT = 1000; //1 second
025: static final int MAX_WAIT = 60000; //1 minute
026:
027: static PrintStream log = null;
028:
029: //Class constants
030: Socket s;
031: int service;
032:
033: /**
034: Creates new TestService object, which will perform particular
035: service on given socket.
036:
037: @param s Socket on which to perform service.
038: @param service Service which to provide.
039: */
040: public TestService(Socket s, int service) {
041: this .s = s;
042: this .service = service;
043: }
044:
045: /**
046: Default constructor.
047: */
048: public TestService() {
049: this .s = null;
050: this .service = -1;
051: }
052:
053: public void run() {
054: try {
055: serve(s, service);
056: } catch (IOException ioe) {
057: log("Exception:" + ioe);
058: ioe.printStackTrace();
059: }
060: try {
061: s.close();
062: } catch (IOException ioe) {
063: }
064: }
065:
066: //Static functions
067: /////////////////
068:
069: /**
070: Maps service name to the integer id, does it in simple
071: linear search manner.
072: @param serviceName Name of the service whose id one needs.
073: @return Integer identifier for this servuce, or -1, if service
074: can't be found.
075: */
076: static public int getServiceId(String serviceName) {
077: serviceName = serviceName.toLowerCase();
078: for (int i = 0; i < serviceNames.length; ++i)
079: if (serviceName.equals(serviceNames[i]))
080: return i;
081:
082: //Couldn't find one.
083: return -1;
084: }
085:
086: /**
087: Performs given service on given socket.
088: <p>
089: Simply looks up and calls associated method.
090: @param s Socket on which to perform service.
091: @param service Id of the service to perform.
092: @return true if service have been found, false otherwise.
093: */
094: static public boolean serve(Socket s, int service)
095: throws IOException {
096: switch (service) {
097: case ECHO:
098: echo(s);
099: break;
100: case DISCARD:
101: discard(s);
102: break;
103: case CHARGEN:
104: chargen(s, CHARGEN_WAIT, MAX_WAIT);
105: break;
106: case CONNECT:
107: connect(s);
108: break;
109: default:
110: log("Unknown service:" + service);
111: return false;
112: }
113: return true;
114: }
115:
116: /**
117: Echos any input on the socket to the output.
118: Echo is being done line by line.
119: @param s Socket on which to perform service.
120: */
121: static public void echo(Socket s) throws IOException {
122: BufferedReader in = new BufferedReader(new InputStreamReader(s
123: .getInputStream()));
124: OutputStream out = s.getOutputStream();
125:
126: log("Starting \"echo\" on " + s);
127:
128: String line = in.readLine();
129: while (line != null) {
130: out.write((line + "\n").getBytes());
131: log(line);
132: line = in.readLine();
133: }
134:
135: log("Echo done.");
136: }
137:
138: /**
139: Reads input from the socket, and does not write anything back.
140: logs input in line by line fashion.
141: @param s Socket on which to perform service.
142: */
143: static public void discard(Socket s) throws IOException {
144: BufferedReader in = new BufferedReader(new InputStreamReader(s
145: .getInputStream()));
146: log("Starting discard on " + s);
147:
148: String line = in.readLine();
149: while (line != null) {
150: log(line);
151: line = in.readLine();
152: }
153:
154: log("Discard finished.");
155: }
156:
157: /**
158: Generates characters and sends them to the socket.
159: <p>
160: Unlike usual chargen (port 19), each next line of the generated
161: output is send after increasingly larger time intervals. It starts
162: from wait_time (ms), and each next time wait time is doubled.
163: Eg. 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 ... well
164: you got the idea.
165: <p>
166: It starts if either connection is clsoed or the wait time grows
167: bigger than max_wait.
168:
169: @param s Socket on which to perform service.
170: @param wait_time Time in ms, from which timing sequence should begin.
171: @param max_wait Time in ms, after reaching timeout greater than this
172: value, chargen will stop.
173: */
174: static public void chargen(Socket s, long wait_time, long max_wait)
175: throws IOException {
176: byte[] buf = chargenSequence.getBytes();
177: int pos = 0;
178: OutputStream out = s.getOutputStream();
179: InputStream in = s.getInputStream();
180: s.setSoTimeout(100); //0.1 ms
181:
182: log("Starting \"chargen\" on " + s);
183: while (true) {
184: log("Sending message.");
185: out.write(buf, pos, buf.length - pos);
186: out.write(buf, 0, pos);
187: out.write("\n".getBytes());
188: pos++;
189: try {
190: if (wait_time > max_wait)
191: break;
192:
193: log("Going to sleep for " + wait_time + " ms.");
194: Thread.currentThread().sleep(wait_time);
195: wait_time *= 2;
196: if (in.read() < 0)
197: break; //Connection closed
198: } catch (InterruptedException ie) {
199: } catch (InterruptedIOException ioe) {
200: }
201: }
202: log("Chargen finished.");
203: }
204:
205: /**
206: Models connect back situation.
207: <p>
208: Reads a line from the socket connection, line should be in the
209: form port service_id. Connects back to the remote host to port
210: specified in the line, if successful performs a service speciefied
211: by id on that new connection. If accept was successful closes the
212: control connection, else outputs error message, and then closes
213: the connection.
214:
215: @param s Control connection.
216: */
217: static public void connect(Socket s) throws IOException {
218: String line = null;
219: Socket sock;
220: int port;
221: int service_id;
222:
223: BufferedReader in = new BufferedReader(new InputStreamReader(s
224: .getInputStream()));
225: OutputStream out = s.getOutputStream();
226:
227: log("Starting \"connect\" on " + s);
228: line = in.readLine();
229: if (line == null)
230: return; //They closed connection
231:
232: java.util.StringTokenizer st = new java.util.StringTokenizer(
233: line);
234: if (st.countTokens() < 2) { //We need at least 'port' and "id"
235: out.write("Expect: port serviceId.\n".getBytes());
236: log("Invalid arguments.");
237: return;
238: }
239: try {
240: port = Integer.parseInt(st.nextToken());
241: service_id = Integer.parseInt(st.nextToken());
242: } catch (NumberFormatException nfe) {
243: out.write("Expect: port serviceId.\n".getBytes());
244: log("Invalid arguments.");
245: return;
246: }
247: try {
248: log("Connecting to " + s.getInetAddress() + ":" + port);
249: sock = new Socket(s.getInetAddress(), port);
250: } catch (IOException ioe) {
251: out
252: .write(("Connect to " + s.getInetAddress() + ":"
253: + port + " failed").getBytes());
254: log("Connect failed.");
255: return;
256: }
257: s.close();
258: log("About to serve " + service_id);
259: serve(sock, service_id);
260: }
261:
262: /**
263: Pipes data from the input stream to the output.
264: @param in Input stream.
265: @param out Output stream.
266: */
267: static public void pipe(InputStream in, OutputStream out)
268: throws IOException {
269: byte[] buf = new byte[BUF_SIZE];
270: int bread = 0;
271: while (bread >= 0) {
272: bread = in.read(buf);
273: out.write(buf, 0, bread);
274: }
275: }
276:
277: /**
278: Performs logging.
279: */
280: static synchronized void log(String s) {
281: if (log != null)
282: log.println(s);
283: }
284:
285: }
|