// Copyright (c) 1999 // The Regents of The University of Michigan // All rights reserved // Permission is granted to use, copy and redistribute this software // for noncommercial education and research purposes, so long as no // fee is charged, and so long as the copyright notice above, this // grant of permission, and the disclaimer below appear in all copies // made; and so long as the name of The University of Michigan is not // used in any advertising or publicity pertaining to the use or // distribution of this software without specific, written prior // authorization. Permission to modify or otherwise create derivative // works of this software is not granted. // // This software is provided as is, without representation as to its // fitness for any purpose, and without warranty of any kind, either // express or implied, including without limitation the implied // warranties of merchantability and fitness for a particular purpose. // The Regents of The University of Michigan shall not be liable for // any damages, including special, indirect, incidental, or consequential // damages, with respect to any claim arising out of or in connection // with the use of the software, even if it has been or is hereafter // advised of the possibility of such damages. // Contact: info@citi.umich.edu /* A very small tcp stack and web server for Cyberflex Access */ import javacard.framework.*; import javacardx.framework.*; public class ip7816 extends Applet { static final byte IP_CLA = (byte)0xfe; static final byte IP_INS = (byte)0xfe; static final byte IP_P1 = (byte)0x00; static final byte IP_P2 = (byte)0x21; static final byte FL_ACK = 0x10; static final byte FL_PSH = 0x8; static final byte FL_RST = 0x4; static final byte FL_SYN = 0x2; static final byte FL_FIN = 0x1; static final byte ST_LISTEN = 0; static final byte ST_ESTAB = 2; static final byte ST_FW1 = 3; static final byte ST_FW2 = 4; static final short CD = ISO.OFFSET_CDATA; static final short MTU = 248; // Fits in a 256 byte apdu static final short WINDOW = 2680; // "TCB" // In a full tcp implementation we would keep track of this per connection. // This implementation only handles one connection at a time. // As a result, very little of this state is actually used after // the reply packet has been sent. short id, tcb_port; // ip id, tcp port byte tcb_st; // state private ip7816() { register(); } public static void install(APDU apdu) { new ip7816(); } public boolean select() { id = 1; tcb_st = ST_LISTEN; return true; } public static void main(String args[]) { ISOException.throwIt((short) 0x7264); } private short ip_input(APDU apdu, byte pkt[]) { short i, len, port, rcvnxt0, rcvnxt1, sndnxt, offset, datlen, ck, d0, d1; byte fl; len = apdu.setIncomingAndReceive(); if (len < 40) return ISO.SW_WRONG_LENGTH; // Packet may have been truncated by ip7816d; find real len len = Util.getShort(pkt, (short)(CD+2)); // If it's not http, just drop it if (Util.getShort(pkt, (short)(CD+22)) != 80) return ISO.SW_NO_ERROR; // Get source port port = Util.getShort(pkt, (short)(CD+20)); // Get the sender's sequence and ack // XXX Note this is 16-bit; we don't handle overflow rcvnxt0 = Util.getShort(pkt, (short)(CD+24)); rcvnxt1 = Util.getShort(pkt, (short)(CD+26)); sndnxt = Util.getShort(pkt, (short)(CD+30)); // Find the payload offset = (short) (((pkt[CD+32] >> 2) & 0x3c) + 20); datlen = (short) (len - offset); len = 40; fl = FL_ACK; apdu.waitExtension(); // Figure out what kind of packet this is, and respond if ((pkt[CD+33] & FL_SYN) != 0) { // SYN sndnxt = 0; rcvnxt1++; fl |= FL_SYN; tcb_st = ST_ESTAB; } else if (datlen > 0) { // incoming data rcvnxt1 += datlen; // Get the url (two chars after "GET /") if (pkt[CD+offset+5] == 0x20) // Turn "/" into "in" d0 = 0x696e; else d0 = Util.getShort(pkt, (short)(CD+offset+5)); // select the file and get its size // if file not found, try "nf" then "in" if (CyberflexFile.selectFile(d0) != ST.SUCCESS && CyberflexFile.selectFile((short)0x6e66) != ST.SUCCESS) CyberflexFile.selectFile((short)0x696e); len += (short) (CyberflexFile.getFileSize() - 16); fl |= FL_PSH; tcb_port = port; } else if ((pkt[CD+33] & FL_FIN) != 0) { // FIN rcvnxt1++; // Don't bother with FIN-WAIT-2, TIME-WAIT, or CLOSED; they just cause trouble tcb_st = ST_LISTEN; } else if ((pkt[CD+33] & FL_ACK) != 0) { // ack with no data if (tcb_port == port && sndnxt > 1) { // calculate no of bytes left to send i = (short) (CyberflexFile.getFileSize() - 16 - (sndnxt - 1)); if (i == 0) { // EOF; send FIN fl |= FL_FIN; tcb_st = ST_FW1; } else if (i > 0) { // not EOF; send next segment len += i; fl |= FL_PSH; } else { // ack of FIN; no reply return ISO.SW_NO_ERROR; } } else return ISO.SW_NO_ERROR; // No reply packet } else return ISO.SW_NO_ERROR; // drop it // Send reply packet if (len > MTU) len = MTU; // Read next segment of data into buffer if (len > 40) CyberflexOS.readBinaryFile(pkt, (short)40, (short)(sndnxt - 1), (short)(len - 40)); // swap source and destination IP addresses Util.arrayCopy(pkt, (short)(CD+16), pkt, (short)12, (short)4); Util.arrayCopy(pkt, (short)(CD+12), pkt, (short)16, (short)4); // clear rest of packet headers for (i = 0; i < 12; i++) pkt[i] = 0; for (i = 20; i < 40; i++) pkt[i] = 0; // Fill in IP header pkt[0] = 0x45; // version, header len Util.setShort(pkt, (short)2, len); Util.setShort(pkt, (short)4, id); pkt[8] = 60; // ttl pkt[9] = 6; // protocol (tcp) apdu.waitExtension(); // Calculate IP header checksum ck = d0 = d1 = 0; for (i = 0; i < 20; i += 2) { d0 += (short) (pkt[i] & 0xff); d1 += (short) (pkt[i+1] & 0xff); } // This works because IP header is too short to overflow high byte ck = (short) ~(((d0 >> 8) & 0xff) + (d0 << 8) + d1); Util.setShort(pkt, (short)10, ck); apdu.waitExtension(); // Fill in TCP header pkt[21] = 80; // Source port Util.setShort(pkt, (short)22, port); Util.setShort(pkt, (short)26, sndnxt); Util.setShort(pkt, (short)28, rcvnxt0); Util.setShort(pkt, (short)30, rcvnxt1); pkt[32] = 0x50; // data offset = 20 (no options) pkt[33] = fl; // flags Util.setShort(pkt, (short)34, WINDOW); // Calculate TCP checksum ck = d0 = d1 = 0; pkt[len] = 0; for (i = 12; i < len; i += 2) { d0 += (short) (pkt[i] & 0xff); d1 += (short) (pkt[i+1] & 0xff); } d1 += 6 + len - 20; ck = (short) ((d0 & 0xff) + ((d1 >> 8) & 0xff)); ck = (short) ~(((d0 >> 8) & 0xff) + (d1 & 0xff) + ((ck >> 8) & 0xff) + (ck << 8)); Util.setShort(pkt, (short)36, ck); // Send return packet apdu.setOutgoingAndSend((short)0, len); return ISO.SW_BYTES_REMAINING_00; } public void process(APDU apdu) { short status = ISO.SW_NO_ERROR; byte b[] = apdu.getBuffer(); // Check cla & ins if (b[ISO.OFFSET_CLA] != IP_CLA) { if (b[ISO.OFFSET_INS] == ISO.INS_SELECT) { if (b[ISO.OFFSET_P1] != 4) status = ISO.SW_WRONG_P1P2; } else if (b[ISO.OFFSET_INS] != 0xc0) status = ISO.SW_CLA_NOT_SUPPORTED; } if (b[ISO.OFFSET_INS] != IP_INS) status = ISO.SW_INS_NOT_SUPPORTED; if (status == ISO.SW_NO_ERROR) { // Incoming IP packet status = ip_input(apdu, b); } if (status != ISO.SW_BYTES_REMAINING_00) ISOException.throwIt(status); } }