// Super Awesome Port Mapping Protocol - Server // (simulated) import java.io.*; import java.net.*; import java.util.*; public class sapmps2020 { public static final int SRVPORT = 6391; //designated SAPMP server port // store successful port mappings: static HashSet mappings = new HashSet(); // record number of connections made by each client ip: static HashMap SCX = new HashMap(); //counts static int reqlimit = 128; // defense against DOS // return available port starting with p as default. public static int getport(int p) { ServerSocket sc; boolean stop = false; try { sc = new ServerSocket(p); // pick p if available sc.setReuseAddress(true); // no TIMEWAIT, allows immediate sc.close(); // reuse of port } catch (IOException e) { try{ sc = new ServerSocket(0); // else pick random port sc.setReuseAddress(true); // no TIMEWAIT, allows immediate p = sc.getLocalPort(); sc.close(); } catch (IOException ee){ /*oops*/ } } return p; }//getport // following functions are also found in Netutils.java // function to convert string format IP into 4-byte array public static byte[] ipbytes(String addr) // "13.45.255.10" { 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 // in C, use inet_aton and inet_ntoa functions. // java sapmps2020 "96.57.41.74" public static void main(String[] args) throws Exception { String nataddr = args[0]; // natbox external ip byte[] NATADDR = new byte[4]; // info received from client: byte[] internalip = new byte[4]; int internalport = 0; int externalport = 0; // requested natbox port to be mapped byte protocol = 0; // 17=udp, 6=tcp Runtime shell = Runtime.getRuntime(); System.out.println("SAPMP SERVER STARTED..."); ServerSocket sfd = new ServerSocket(SRVPORT); Socket cfd = null; // comm socket sfd.setReuseAddress(true); // turn off TIME_WAIT while (true) { cfd = sfd.accept(); System.out.println("sapmp connect from "+cfd.getInetAddress()); cfd.setSoTimeout(2500); // timeout on reads for(int i=0;i<4;i++) internalip[i]=0; // clear buffer for read internalport = externalport = 0; protocol = 0; boolean ok = true; // checks if request is valid boolean timedout = false; for(int i=0;i<4;i++) NATADDR[i]=0; // clear for error codes int erri = 0; // error index into NATADDR, will wrap Integer cx = SCX.get(""+cfd.getInetAddress()); if (cx==null) cx = 0; cx += 1; if (cx>reqlimit) { ok = false; NATADDR[erri++%4]=6; System.out.println(cfd.getInetAddress()+" has made too many requests"); } else SCX.put(""+cfd.getInetAddress(),cx); DataInputStream din; // binary communicators DataOutputStream dout; din = new DataInputStream(cfd.getInputStream()); dout = new DataOutputStream(cfd.getOutputStream()); try { int c = 0; // counts bytes read while (c!=4) c += din.read(internalip,c,4-c); internalport = din.readUnsignedShort(); externalport = din.readUnsignedShort(); protocol = din.readByte(); } catch (SocketTimeoutException ste) { timedout=true; } catch (Exception ee) { ok=false; } // print trace System.out.printf("...received targetport %d, targetip %s, externport %d and protocol %d\n",internalport,ipstring(internalip),externalport,protocol); if (externalport<1024) { ok=false; NATADDR[erri++%4]=3;} if (protocol!=6 && protocol!=17) {ok = false; NATADDR[erri++%4]=4;} if (internalip[0]!=10) {ok = false; NATADDR[erri++%4]=2;} String internip = ipstring(internalip); fwdinfo req = new fwdinfo(internip,externalport,protocol); if (mappings.contains(req)) // port already mapped {ok=false; NATADDR[erri++%4]=5;} if (timedout) {ok=false; NATADDR[erri++%4]=1;} if (ok) // still ok { NATADDR = ipbytes(nataddr); int eport = getport(externalport); // issue iptables command String cmd = "/sbin/iptables -t nat -A PREROUTING "; cmd += "-p "+protocol+" --dport "+eport+" "; cmd += "-j DNAT --to "+internip+":"+internalport; // shell.exec(cmd); // execute iptables command System.out.println("--issuing command: "+cmd); //echo // send back mapped port and ip: dout.writeShort((short)eport); dout.write(NATADDR,0,4); mappings.add(req); // add mapping to port to hashset }//ok else // not ok { System.out.print("bad input from socket: "); //for(int i=0;i<4;i++) System.out.print(NATADDR[i]+" "); System.out.println(); dout.writeShort(0); // indicates failed map. dout.write(NATADDR,0,4); } din.close(); dout.close(); cfd.close(); } // server loop }//main }//sapmps2020 class fwdinfo // forwarding info, for insertion into hashtable { String internip; int externport; byte protocol; public fwdinfo(String i, int p, byte r) {internip=i; externport=p; protocol=r;} public boolean equals(Object x) { fwdinfo f = (fwdinfo)x; return f.externport==externport && f.protocol==protocol && internip.equals(f.internip); } public int hashCode() { int i = 0; if (protocol==6) i = 1; return externport*2+i; } }