0

I'm trying to write a BOOTP Client/Server in Java, and one of the specifications in the RFC is that the client can write the nickname of the server it wants to connect to. If the packet reaches a server that is not the one specified, it should discard the bootrequest.

I have three classes (BOOTPClient, BOOTPServer and BOOTPMessage). As of now, I've successfully sent the bootrequest from the client to the server, who recieves it and starts to process it.

The problem is here:

String sname = new String(btpmsg.getSname());
String serverName = args[0];
boolean discard = false;
System.out.println("|"+serverName+"|"+sname+"|");
if (!sname.equalsIgnoreCase("") && !sname.equalsIgnoreCase(serverName)) {
    discard = true;
    System.out.println("The request was not for this server. Server requested: "+sname);
}

This meaning: if the string is not null (in which case we don't care about what server it arrives to), make sure it's the correct server. As you can see, I printed both strings between "|" to be 100% sure that there aren't whitespaces or anything that could mess up the comparison. Yet, it enters into if block and executes the sentences inside. It happens both if the string is the same as the server name and if it's blank.

To clarify: btpmsg is an instance of the BOOTPMsg class, the server name is stored as an array of bytes (everything is coded into a byte array with ByteArrayOutputStream to send it with DatagramPacket) and then converted to a String. I don't know where the problem may be.

A sample of the output:

Server (called with "java BOOTPServer bootpserver"):

Server bootpserver running. Awaiting requests.
Bootrequest received.
|bootpserver|bootpserver|
The request was not for this server. Server requested: bootpserver

Client:

Enter your IP if known. Otherwise, press enter.

Enter the IP of the server if known. Otherwise, press enter.

Enter the name of the server if known. Otherwise, press enter.
bootpserver
Enter the file name if known. Otherwise, press enter.

I'd appreciate any help, because I don't know what else I can look for errors. My best bet was on using the "write" method on ByteArrayOutputStream or readFully in DataInputStream, but all others parameters work fine. Thanks a lot in advance.

binkypv
  • 43
  • 1
  • 7
  • 1
    Your code worked fine to me when I assigned to both variables `String sname = new String("bootpserver");String serverName = new String ("bootpserver");` Perhaps one of the strings you are comparing contains some invisible character? Try to print the String lengths. – Eran Oct 26 '14 at 11:39
  • Additionally print the contents of `String.toCharArray()` in numeric form. – Marko Topolnik Oct 26 '14 at 11:42
  • Woah, 11 and 64. That's quite a difference in length. I must be reading the strings from the byte array in a wrong way. I should check where I'm doing this wrong. Thanks a lot!! – binkypv Oct 26 '14 at 11:43
  • My bet is you'll find a lot of 0-chars a the end of your string. `getSname()` returns a `char[]`, doesn't it? And it's of fixed length (64). So you'll first need to find the first 0-char and use the explicit offset, length string constructor. – Marko Topolnik Oct 26 '14 at 11:44
  • `getSname()` returns a `byte[]`, but it's exactly as you said. Thanks a lot, really. I was struggling with this for quite a long time, and it was the only thing that didn't work! – binkypv Oct 26 '14 at 11:48
  • If it's a `byte[]`, then you shouldn't let the encoding be decided by the platform. Use the explicit constructor, passing in `StandardCharsets.ISO_8859_1` (it's the same as ASCII, but with meanings given to the upper 128 codes as well). – Marko Topolnik Oct 26 '14 at 11:50

1 Answers1

1

Your getSname() method returns a byte[] of fixed length 64, which is the specified length of the sname field in the BOOTP protocol. When you pass it directly to the String(byte[]) constructor, you get a string right-padded to length 64 with zero-characters.

To fix this, you need to find the first zero byte in the array and use that when constructing the string. Additionally make sure you pass in an explicit character encoding; otherwise you are at the mercy of the platform default.

So, use code such as this:

sname2string(byte[] sname) {
   int length = 0;
   while (length < 64 && sname[length] != 0) length++;
   return new String(sname, 0, length, StandardCharsets.ISO_8859_1);
}
Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • Thanks a lot. I made a similar method that works for generic `byte[]` based on this. It works like a charm, and I've been hours wondering what could be wrong in the code. Thanks once again! – binkypv Oct 26 '14 at 12:06