CSC 175 Programming Assignment 1 SAPMP: Super Awesome Port Mapping Protocol For your first programming assignment you must implement a tcp client that would work with my server. The java source code of my server has been provided for you. You can write your client program in any language, but java is recommended. But don't just copy and paste from the server program without thinking if it really makes sense to have in your client program (which will be somewhat simpler than the server). The purpose of the assignment is to design our own protocol that works similarly in principle to the NAT-PMP protocol, which allows a client behind a NAT box to dynamically request external ports be forward to it. The SAPMP server is running on 10.1.0.3. But you can also download it and run it on your localhost. The SAPMP server must be run as root (unless you comment out the iptables command). The SAPMP server listens on port 6391. This is the fixed SAPMP server port. The SAPMP server expects a connecting client to send it the following four pieces of information, all in binary form: A. 4 bytes representing the internal (client) host's ip address B. 2 bytes representing the interal host's port, that packets need to be mapped to. C. 2 bytes representing the requested port on the NAT box that should be mapped to the internal port. D. 1 byte representing the protocol (6=tcp, 17=udp). Only 6 and 17 are allowed. For example, if A = 10.1.0.5, B = 80, C = 8000, D = 6, then the server, which is running on a NAT box, will execute the command iptables -t nat -A PREROUTING -p 6 --dport 8000 -j DNAT --to 10.1.0.5:80 If C (the desired external port) is unavailable, the server will pick some unused port instead. Additional restrictions: C cannot be less than 1024 (no reserved port on the NAT box can be hijacked). Address A must be in 10.0.0.0/8, so the DNAT will only redirect a packet inwards - into the internal network and not to who-knows where (but this is also preventable by iptables -I FORWARD 1 -i eth0 -o eth0 -j DROP). Also, since "-A" is used in the iptables rule, any previous rule that mapped the same external port will NOT be overridden. The SAPMP server will send back the following 2 pieces of information: E. 2 bytes representing the external port that was successfully mapped. (this could be different from the requested port if it's unavailable). F. 4 bytes representing the external ip address of the NAT box. If the port mapping failed (because of invalid input) then E will be 0. The idea is that the internal host can then tell everyone else how to open a connection to it from outside of the NATed network. The program is a bit difficult because we are doing binary instead of text communication. But using strings is vulnerable to attacks such as string injection, and is also inefficient. You therefore need to use java's commands for reading/writing with DataInputStream and DataOutputStream very carefully. For example, given DataInputStream din, int p = din.readUnsignedShort() reads in 2 bytes and convert it to a signed 4-byte integer (because ports are positive). To write an integer p to DataOutputStream dout as a short, you need to type-cast it first: dout.writeShort((short)p); When you type cast an integer such as 60000 into a short, the short will become a negative number. Don't worry about it: no bits were altered, and you'll get the same positive number back if on the other side you read the two bytes using .readUnsignedShort(). Given byte[] buffer= new byte[4]; din.read(buffer,0,4) // will read 4 bytes into buffer starting at index 0. Fortunately, if you use Java you don't have to worry about byte-ordering conversion. If you use another language, you'll also have to make sure that all multi-byte numerical values are converted the network byte order form before its sent over a socket, because that's what the server expects. See sample programs on homepage for C, Perl and Python versions of tcp client and server programs. You client should print the information received from the server so that you can see it's working. One tricky part of the assignment is to convert a string representation of an ip address, like "10.1.0.5" into a 4-byte array (byte[]), and vice versa. I've written these functions for you (Java might also have these built-in, check the InetAddress class on the API docs). // function to convert string format IP into 4-byte array public static byte[] ipbytes(String addr) { byte[] B = new byte[4]; // array to be constructed String[] S = addr.split("\\p{Punct}",4); // splits string into 4 parts //System.out.printf("after split: %s:%s:%s:%s\n",S[0],S[1],S[2],S[3]); for(int i=0;i<4;i++) B[i] = (byte)Integer.parseInt(S[i]); return B; } //ipbytes //function to return string-format IP address given byte array: public static String ipstring(byte[] B) { String addr = ""; // string to be returned for(int i=0;i<4;i++) { int x = B[i]; if (x<0) x = x+256; // signed to unsigned conversion addr = addr + ""+ x; if (i<3) addr = addr+"."; } return addr; }// ipstring put these functions into your class