001: /*
002: * Copyright (c) 2000 by Matt Welsh and The Regents of the University of
003: * California. All rights reserved.
004: *
005: * Permission to use, copy, modify, and distribute this software and its
006: * documentation for any purpose, without fee, and without written agreement is
007: * hereby granted, provided that the above copyright notice and the following
008: * two paragraphs appear in all copies of this software.
009: *
010: * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
011: * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
012: * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
013: * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
014: *
015: * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
016: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
017: * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
018: * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
019: * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
020: *
021: * Author: Matt Welsh <mdw@cs.berkeley.edu>
022: *
023: */
024:
025: package seda.sandStorm.lib.Gnutella;
026:
027: import seda.sandStorm.api.*;
028: import seda.sandStorm.lib.aSocket.*;
029: import java.util.*;
030: import java.io.*;
031: import java.net.*;
032:
033: /**
034: * This is an internal class, responsible for generating GnutellaPacket
035: * objects from raw socket data. It encapsulates the core packet-processing
036: * code in the Gnutella protocol.
037: *
038: * @author Matt Welsh
039: */
040: class GnutellaPacketReader implements GnutellaConst {
041:
042: private static final boolean DEBUG = false;
043:
044: private static final int STATE_READ_HEADER = 0;
045: private static final int STATE_READ_PAYLOAD = 1;
046: private int state;
047:
048: private int cur_offset, packet_offset;
049: private byte pktdata[];
050:
051: private GnutellaGUID guid;
052: private int function;
053: private int ttl;
054: private int hops;
055: private int payload_length;
056:
057: private byte header[];
058: private byte payload[];
059:
060: private Vector completePackets;
061:
062: GnutellaPacketReader() {
063: state = STATE_READ_HEADER;
064: header = new byte[PACKET_HEADER_SIZE];
065: cur_offset = 0;
066: completePackets = new Vector(1);
067: }
068:
069: void pushPacket(ATcpInPacket pkt) throws IOException {
070:
071: packet_offset = 0;
072: pktdata = pkt.getBytes();
073:
074: if (DEBUG)
075: System.err.println("GPR: pushPacket called, size "
076: + pktdata.length);
077:
078: boolean proceed = true;
079:
080: try {
081: while (proceed) {
082: switch (state) {
083: case STATE_READ_HEADER:
084: proceed = doReadHeader();
085: break;
086:
087: case STATE_READ_PAYLOAD:
088: proceed = doReadPayload();
089: break;
090:
091: default:
092: throw new RuntimeException(
093: "Bad state in GnutellaPacketReader - this is a bug, please contact <mdw@cs.berkeley.edu>");
094: }
095: }
096: } catch (IOException e) {
097: // If an error occurs during processing, just drop everything
098: // and wait for the next packet
099: reset();
100: throw e;
101: }
102: }
103:
104: // Used to reset after an error
105: private void reset() {
106: cur_offset = 0;
107: state = STATE_READ_HEADER;
108: }
109:
110: private boolean doReadHeader() throws IOException {
111: if (DEBUG)
112: System.err.println("GPR: doReadHeader called, cur "
113: + cur_offset + ", pkt " + packet_offset);
114:
115: int tocopy = Math.min(header.length - cur_offset,
116: pktdata.length - packet_offset);
117: if (tocopy != 0) {
118: System.arraycopy(pktdata, packet_offset, header,
119: cur_offset, tocopy);
120: cur_offset += tocopy;
121: packet_offset += tocopy;
122: }
123:
124: if (cur_offset == PACKET_HEADER_SIZE) {
125: processHeader();
126: cur_offset = 0;
127: if (payload_length != 0) {
128: payload = new byte[payload_length];
129: state = STATE_READ_PAYLOAD;
130: return true;
131: } else {
132: createPacket();
133: cur_offset = 0;
134: state = STATE_READ_HEADER;
135: return true;
136: }
137: } else {
138: return false;
139: }
140: }
141:
142: private boolean doReadPayload() throws IOException {
143: if (DEBUG)
144: System.err.println("GPR: doReadPayload called, cur "
145: + cur_offset + ", pkt " + packet_offset);
146:
147: int tocopy = Math.min(payload_length - cur_offset,
148: pktdata.length - packet_offset);
149: if (tocopy != 0) {
150: System.arraycopy(pktdata, packet_offset, payload,
151: cur_offset, tocopy);
152: cur_offset += tocopy;
153: packet_offset += tocopy;
154: }
155:
156: if (cur_offset == payload_length) {
157: createPacket();
158: cur_offset = 0;
159: state = STATE_READ_HEADER;
160: return true;
161: } else {
162: return false;
163: }
164: }
165:
166: private void processHeader() throws IOException {
167: guid = new GnutellaGUID(header, 0);
168: function = header[16];
169: ttl = header[17];
170: hops = header[18];
171: payload_length = GnutellaPacket.readLEInt(header, 19);
172: if ((MAX_PAYLOAD_SIZE != -1)
173: && (payload_length > MAX_PAYLOAD_SIZE)) {
174: // Drop packet!
175: throw new IOException("Invalid payload length "
176: + payload_length);
177: }
178: if (payload_length < 0) {
179: // Drop packet!
180: throw new IOException("Invalid payload length "
181: + payload_length);
182: }
183:
184: if (DEBUG)
185: System.err.println("GPR: read header, function "
186: + Integer.toHexString(function & 0xff) + ", ttl "
187: + ttl + ", hops " + hops + ", payload_len "
188: + payload_length);
189: }
190:
191: GnutellaPacket getGnutellaPacket() throws IOException {
192: synchronized (completePackets) {
193: GnutellaPacket gp;
194: try {
195: gp = (GnutellaPacket) completePackets.firstElement();
196: } catch (NoSuchElementException e) {
197: return null;
198: }
199: if (gp != null) {
200: completePackets.removeElementAt(0);
201: return gp;
202: } else {
203: return null;
204: }
205: }
206: }
207:
208: void createPacket() throws IOException {
209: GnutellaPacket gp;
210:
211: switch (function) {
212: case GNUTELLA_FN_PING:
213: gp = new GnutellaPingPacket(guid, ttl, hops);
214: break;
215:
216: case GNUTELLA_FN_PONG:
217: if (payload == null)
218: throw new IOException("pong packet has null payload");
219: gp = new GnutellaPongPacket(guid, ttl, hops, payload);
220: break;
221:
222: case GNUTELLA_FN_PUSH:
223: if (payload == null)
224: throw new IOException("push packet has null payload");
225: gp = new GnutellaPushPacket(guid, ttl, hops, payload);
226: break;
227:
228: case GNUTELLA_FN_QUERY:
229: if (payload == null)
230: throw new IOException("query packet has null payload");
231: gp = new GnutellaQueryPacket(guid, ttl, hops, payload);
232: break;
233:
234: case GNUTELLA_FN_QUERYHITS:
235: if (payload == null)
236: throw new IOException(
237: "query hits packet has null payload");
238: gp = new GnutellaQueryHitsPacket(guid, ttl, hops, payload);
239: break;
240:
241: default:
242: throw new IOException(
243: "GnutellaPacket got illegal function code "
244: + Integer.toHexString(function));
245: }
246:
247: completePackets.addElement(gp);
248: }
249:
250: }
|