package net.i2p.i2ptunnel.socks;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
/**
* Save the SOCKS header from a datagram
* Ref: RFC 1928
*
* @author zzz
*/
public class SOCKSHeader {
/**
* @param data the whole packet
*/
public SOCKSHeader(byte[] data) {
if (data.length <= 8)
throw new IllegalArgumentException("Header too short: " + data.length);
if (data[0] != 0 || data[1] != 0)
throw new IllegalArgumentException("Not a SOCKS datagram?");
if (data[2] != 0)
throw new IllegalArgumentException("We can't handle fragments!");
int headerlen = 0;
int addressType = data[3];
if (addressType == 1) {
// this will fail in getDestination()
headerlen = 6 + 4;
} else if (addressType == 3) {
headerlen = 6 + 1 + (data[4] & 0xff);
} else if (addressType == 4) {
// this will fail in getDestination()
// but future garlicat partial hash lookup possible?
headerlen = 6 + 16;
} else {
throw new IllegalArgumentException("Unknown address type: " + addressType);
}
if (data.length < headerlen)
throw new IllegalArgumentException("Header too short: " + data.length);
this.header = new byte[headerlen];
System.arraycopy(data, 0, this.header, 0, headerlen);
}
private static final byte[] beg = {0,0,0,3,60};
private static final byte[] end = {0,0};
/**
* Make a dummy header from a dest,
* for those cases where we want to receive unsolicited datagrams.
* Unused for now.
*/
public SOCKSHeader(Destination dest) {
this.header = new byte[beg.length + 60 + end.length];
System.arraycopy(beg, 0, this.header, 0, beg.length);
String b32 = dest.toBase32();
System.arraycopy(DataHelper.getASCII(b32), 0, this.header, beg.length, 60);
System.arraycopy(end, 0, this.header, beg.length + 60, end.length);
}
public String getHost() {
int addressType = this.header[3];
if (addressType != 3)
return null;
int namelen = (this.header[4] & 0xff);
byte[] nameBytes = new byte[namelen];
System.arraycopy(this.header, 5, nameBytes, 0, namelen);
return DataHelper.getUTF8(nameBytes);
}
public Destination getDestination() {
String name = getHost();
if (name == null)
return null;
// the naming service does caching (thankfully)
return I2PAppContext.getGlobalContext().namingService().lookup(name);
}
public byte[] getBytes() {
return header;
}
private byte[] header;
}