From: bdemsky Date: Mon, 25 Jul 2016 00:47:21 +0000 (-0700) Subject: add some comments X-Git-Url: http://plrg.eecs.uci.edu/git/?p=iotcloud.git;a=commitdiff_plain;h=7ea60d50a662ec2b69b3a09e1b00127c9cea19a7 add some comments --- diff --git a/src/java/iotcloud/CloudComm.java b/src/java/iotcloud/CloudComm.java index 2c9e0c7..7a0583d 100644 --- a/src/java/iotcloud/CloudComm.java +++ b/src/java/iotcloud/CloudComm.java @@ -6,6 +6,14 @@ import javax.crypto.*; import javax.crypto.spec.*; import java.security.SecureRandom; +/** + * This class provides a communication API to the webserver. It also + * validates the HMACs on the slots and handles encryption. + * @author Brian Demsky + * @version 1.0 + */ + + class CloudComm { String baseurl; Cipher encryptcipher; @@ -15,15 +23,26 @@ class CloudComm { SecretKeySpec key; static final int SALT_SIZE = 8; - + /** + * Empty Constructor needed for child class. + */ + CloudComm() { } + /** + * Constructor for actual use. Takes in the url and password. + */ + CloudComm(String _baseurl, String password) { this.baseurl=_baseurl; initCloud(password); } + /** + * Generates Key from password. + */ + private void initKey(String password) { try { salt=new byte[SALT_SIZE]; @@ -36,6 +55,10 @@ class CloudComm { } } + /** + * Inits the HMAC generator. + */ + private void initCloud(String password) { try { initKey(password); @@ -46,7 +69,11 @@ class CloudComm { throw new Error("Failed To Initialize Ciphers"); } } - + + /* + * Builds the URL for the given request. + */ + private URL buildRequest(boolean isput, long sequencenumber, long maxentries) throws IOException { String reqstring=isput?"req=putslot":"req=getslot"; String urlstr=baseurl+"?"+reqstring+"&seq="+sequencenumber; @@ -55,6 +82,12 @@ class CloudComm { return new URL(urlstr); } + /* + * API for putting a slot into the queue. Returns null on success. + * On failure, the server will send slots with newer sequence + * numbers. + */ + public Slot[] putSlot(Slot slot, int max) { try { long sequencenumber=slot.getSequenceNumber(); @@ -87,12 +120,19 @@ class CloudComm { } /* - Cipher encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - encryptCipher.init(Cipher.ENCRYPT_MODE, secret); - Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - decryptCipher.init(Cipher.DECRYPT_MODE, secret); - */ - + Cipher encryptCipher = + Cipher.getInstance("AES/CBC/PKCS5Padding"); + encryptCipher.init(Cipher.ENCRYPT_MODE, secret); + Cipher decryptCipher = + Cipher.getInstance("AES/CBC/PKCS5Padding"); + decryptCipher.init(Cipher.DECRYPT_MODE, secret); + */ + + /** + * Request the server to send all slots with the given + * sequencenumber or newer. + */ + public Slot[] getSlots(long sequencenumber) { try { URL url=buildRequest(false, sequencenumber, 0); @@ -115,7 +155,12 @@ class CloudComm { } } - Slot[] processSlots(DataInputStream dis) throws IOException { + /** + * Method that actually handles building Slot objects from the + * server response. Shared by both putSlot and getSlots. + */ + + private Slot[] processSlots(DataInputStream dis) throws IOException { int numberofslots=dis.readInt(); int[] sizesofslots=new int[numberofslots]; Slot[] slots=new Slot[numberofslots]; diff --git a/src/java/iotcloud/Entry.java b/src/java/iotcloud/Entry.java index 4ff9b06..cef615f 100644 --- a/src/java/iotcloud/Entry.java +++ b/src/java/iotcloud/Entry.java @@ -1,11 +1,22 @@ package iotcloud; import java.nio.ByteBuffer; +/** + * Generic class that wraps all the different types of information + * that can be stored in a Slot. + * @author Brian Demsky + * @version 1.0 + */ + abstract class Entry implements Liveness { static final byte TypeKeyValue = 1; static final byte TypeLastMessage = 2; static final byte TypeRejectedMessage = 3; static final byte TypeTableStatus = 4; + + /* Records whether the information is still live or has been + superceded by a newer update. */ + private boolean islive = true; private Slot parentslot; @@ -13,6 +24,11 @@ abstract class Entry implements Liveness { parentslot = _parentslot; } + /** + * Static method for decoding byte array into Entry objects. First + * byte tells the type of entry. + */ + static Entry decode(Slot slot, ByteBuffer bb) { byte type=bb.get(); switch(type) { @@ -33,18 +49,40 @@ abstract class Entry implements Liveness { } } + /** + * Returns true if the Entry object is still live. + */ + boolean isLive() { return islive; } + /** + * Flags the entry object as dead. Also decrements the live count + * of the parent slot. + */ + void setDead() { islive = false; parentslot.decrementLiveCount(); } + /** + * Serializes the Entry object into the byte buffer. + */ + abstract void encode(ByteBuffer bb); + /** + * Returns the size in bytes the entry object will take in the byte + * array. + */ + abstract int getSize(); + /** + * Returns a byte encoding the type of the entry object. + */ + abstract byte getType(); } diff --git a/src/java/iotcloud/IoTString.java b/src/java/iotcloud/IoTString.java index c0e4197..19ebee3 100644 --- a/src/java/iotcloud/IoTString.java +++ b/src/java/iotcloud/IoTString.java @@ -2,6 +2,14 @@ package iotcloud; import java.util.Arrays; +/** + * IoTString is wraps the underlying byte string. We don't use the + * standard String class as we have bytes and not chars. + * @author Brian Demsky + * @version 1.0 + */ + + final public class IoTString { byte[] array; int hashcode; @@ -9,16 +17,32 @@ final public class IoTString { private IoTString() { } + /** + * Builds an IoTString object around the byte array. This + * constructor makes a copy, so the caller is free to modify the byte array. + */ + public IoTString(byte[] _array) { array=(byte[]) _array.clone(); hashcode=Arrays.hashCode(array); } + /** + * Converts the String object to a byte representation and stores it + * into the IoTString object. + */ + public IoTString(String str) { array=str.getBytes(); hashcode=Arrays.hashCode(array); } + /** + * Internal methods to build an IoTString using the byte[] passed + * in. Caller is responsible for ensuring the byte[] is never + * modified. + */ + static IoTString shallow(byte[] _array) { IoTString i=new IoTString(); i.array = _array; @@ -26,22 +50,43 @@ final public class IoTString { return i; } + /** + * Internal method to grab a reference to our byte array. Caller + * must not modify it. + */ + byte[] internalBytes() { return array; } + /** + * Returns the hashCode as computed by Arrays.hashcode(byte[]). + */ + public int hashCode() { return hashcode; } + /** + * Returns a String representation of the IoTString. + */ + public String toString() { return new String(array); } + /** + * Returns a copy of the underlying byte string. + */ + public byte[] getBytes() { return (byte[]) array.clone(); } + /** + * Returns true if two byte strings have the same content. + */ + public boolean equals(Object o) { if (o instanceof IoTString) { IoTString i=(IoTString)o; @@ -50,6 +95,10 @@ final public class IoTString { return false; } + /** + * Returns the length in bytes of the IoTString. + */ + public int length() { return array.length; } diff --git a/src/java/iotcloud/KeyValue.java b/src/java/iotcloud/KeyValue.java index 51d84eb..abd1cd5 100644 --- a/src/java/iotcloud/KeyValue.java +++ b/src/java/iotcloud/KeyValue.java @@ -1,6 +1,12 @@ package iotcloud; import java.nio.ByteBuffer; +/** + * KeyValue entry for Slot. + * @author Brian Demsky + * @version 1.0 + */ + class KeyValue extends Entry { private IoTString key; private IoTString value; diff --git a/src/java/iotcloud/LastMessage.java b/src/java/iotcloud/LastMessage.java index b96a483..3e6caca 100644 --- a/src/java/iotcloud/LastMessage.java +++ b/src/java/iotcloud/LastMessage.java @@ -2,6 +2,13 @@ package iotcloud; import java.nio.ByteBuffer; +/** + * This Entry records the last message sent by a given machine. + * @author Brian Demsky + * @version 1.0 + */ + + class LastMessage extends Entry { private long machineid; private long seqnum; diff --git a/src/java/iotcloud/Liveness.java b/src/java/iotcloud/Liveness.java index 1f241a6..2c840e4 100644 --- a/src/java/iotcloud/Liveness.java +++ b/src/java/iotcloud/Liveness.java @@ -1,4 +1,11 @@ package iotcloud; +/** + * Interface common to both classes that record information about the + * last message sent by a machine. (Either a Slot or a LastMessage. + * @author Brian Demsky + * @version 1.0 + */ + interface Liveness { } diff --git a/src/java/iotcloud/RejectedMessage.java b/src/java/iotcloud/RejectedMessage.java index 167521f..0f0bfd0 100644 --- a/src/java/iotcloud/RejectedMessage.java +++ b/src/java/iotcloud/RejectedMessage.java @@ -1,11 +1,25 @@ package iotcloud; import java.nio.ByteBuffer; +/** + * Entry for tracking messages that the server rejected. We have to + * make sure that all clients know that this message was rejected to + * prevent the server from reusing these messages in an attack. + * @author Brian Demsky + * @version 1.0 + */ + + class RejectedMessage extends Entry { + /* Machine identifier */ private long machineid; - private long oldseqnum; //Oldest seqnum in range - private long newseqnum; //Newest seqnum in range (inclusive) - private boolean equalto; //Is message sent or not sent by machineid + /* Oldest sequence number in range */ + private long oldseqnum; + /* Newest sequence number in range */ + private long newseqnum; + /* Is the machine identifier of the relevant slots equal to (or not + * equal to) the specified machine identifier. */ + private boolean equalto; RejectedMessage(Slot slot, long _machineid, long _oldseqnum, long _newseqnum, boolean _equalto) { super(slot); diff --git a/src/java/iotcloud/Slot.java b/src/java/iotcloud/Slot.java index 9de69db..6604894 100644 --- a/src/java/iotcloud/Slot.java +++ b/src/java/iotcloud/Slot.java @@ -4,18 +4,36 @@ import java.nio.ByteBuffer; import javax.crypto.Mac; import java.util.Arrays; +/** + * Data structuring for holding Slot information. + * @author Brian Demsky + * @version 1.0 + */ + class Slot implements Liveness { + /** Sets the slot size. */ static final int SLOT_SIZE=2048; + /** Sets how many bytes we reserve. */ static final int RESERVED_SPACE=64; + /** Sets the size for the HMAC. */ static final int HMAC_SIZE=32; + /** Sequence number of the slot. */ private long seqnum; + /** HMAC of previous slot. */ private byte[] prevhmac; + /** HMAC of this slot. */ private byte[] hmac; + /** Machine that sent this slot. */ private long machineid; + /** Vector of entries in this slot. */ private Vector entries; + /** Pieces of information that are live. */ private int livecount; + /** Flag that indicates whether this slot is still live for + * recording the machine that sent it. */ private boolean seqnumlive; + /** Number of bytes of free space. */ private int freespace; Slot(long _seqnum, long _machineid, byte[] _prevhmac, byte[] _hmac) { @@ -51,11 +69,19 @@ class Slot implements Liveness { freespace -= e.getSize(); } + /** + * Returns true if the slot has free space to hold the entry without + * using its reserved space. */ + boolean hasSpace(Entry e) { int newfreespace = freespace - e.getSize(); return newfreespace > RESERVED_SPACE; } + /** + * Returns true if the slot can fit the entry potentially using the + * reserved space. */ + boolean canFit(Entry e) { int newfreespace = freespace - e.getSize(); return newfreespace >= 0; @@ -92,7 +118,8 @@ class Slot implements Liveness { byte[] encode(Mac mac) { byte[] array=new byte[SLOT_SIZE]; ByteBuffer bb=ByteBuffer.wrap(array); - bb.position(HMAC_SIZE); //Leave space for the HMACs + /* Leave space for the slot HMAC. */ + bb.position(HMAC_SIZE); bb.put(prevhmac); bb.putLong(seqnum); bb.putLong(machineid); @@ -100,7 +127,7 @@ class Slot implements Liveness { for(Entry entry:entries) { entry.encode(bb); } - //Compute our HMAC + /* Compute our HMAC */ mac.update(array, HMAC_SIZE, array.length-HMAC_SIZE); byte[] realmac=mac.doFinal(); hmac = realmac; @@ -109,10 +136,20 @@ class Slot implements Liveness { return array; } + /** + * Returns the empty size of a Slot. Includes 2 HMACs, the machine + * identifier, the sequence number, and the number of entries. + */ int getBaseSize() { return 2*HMAC_SIZE+2*Long.BYTES+Integer.BYTES; } + /** + * Returns the live set of entries for this Slot. Generates a fake + * LastMessage entry to represent the information stored by the slot + * itself. + */ + Vector getLiveEntries() { Vector liveEntries=new Vector(); for(Entry entry: entries) @@ -125,23 +162,44 @@ class Slot implements Liveness { return liveEntries; } + /** + * Returns the sequence number of the slot. + */ + long getSequenceNumber() { return seqnum; } + /** + * Returns the machine that sent this slot. + */ + long getMachineID() { return machineid; } + /** + * Records that a newer slot records the fact that this slot was + * sent by the relevant machine. + */ + void setDead() { decrementLiveCount(); seqnumlive=false; } + /** + * Update the count of live entries. + */ + void decrementLiveCount() { livecount--; } + /** + * Returns whether the slot stores any live information. + */ + boolean isLive() { return livecount > 0; } diff --git a/src/java/iotcloud/SlotBuffer.java b/src/java/iotcloud/SlotBuffer.java index 3d5a465..5d51c1d 100644 --- a/src/java/iotcloud/SlotBuffer.java +++ b/src/java/iotcloud/SlotBuffer.java @@ -1,5 +1,11 @@ package iotcloud; +/** + * Buffer that holds the live set of slots. + * @author Brian Demsky + * @version 1.0 + */ + class SlotBuffer { static final int DEFAULT_SIZE = 128; diff --git a/src/java/iotcloud/SlotIndexer.java b/src/java/iotcloud/SlotIndexer.java index 00a82bf..cecdf2d 100644 --- a/src/java/iotcloud/SlotIndexer.java +++ b/src/java/iotcloud/SlotIndexer.java @@ -1,5 +1,12 @@ package iotcloud; +/** + * Slot indexer allows slots in both the slot buffer and the new + * server response to looked up in a consistent fashion. + * @author Brian Demsky + * @version 1.0 + */ + class SlotIndexer { private Slot[] updates; private SlotBuffer buffer; diff --git a/src/java/iotcloud/Table.java b/src/java/iotcloud/Table.java index ced979d..28434c5 100644 --- a/src/java/iotcloud/Table.java +++ b/src/java/iotcloud/Table.java @@ -3,6 +3,13 @@ import java.util.HashMap; import java.util.Arrays; import java.util.Vector; +/** + * IoTTable data structure. Provides client inferface. + * @author Brian Demsky + * @version 1.0 + */ + + final public class Table { private int numslots; private HashMap table=new HashMap(); @@ -35,7 +42,7 @@ final public class Table { Slot[] newslots=cloud.getSlots(sequencenumber+1); validateandupdate(newslots, true); } - + public void update() { Slot[] newslots=cloud.getSlots(sequencenumber+1); @@ -57,7 +64,8 @@ final public class Table { Slot[] array=cloud.putSlot(s, numslots); if (array == null) { array = new Slot[] {s}; - validateandupdate(array, true); // update data structure + /* update data structure */ + validateandupdate(array, true); } else { throw new Error("Error on initialization"); } @@ -85,7 +93,7 @@ final public class Table { } if ((numslots - buffer.size()) < FREE_SLOTS) { - //have to check whether we have enough free slots + /* have to check whether we have enough free slots */ long fullfirstseqn = buffer.getNewestSeqNum() + 1 - numslots; seqn = fullfirstseqn < 1?1:fullfirstseqn; for(int i=0; i < FREE_SLOTS; i++, seqn++) { @@ -141,7 +149,8 @@ search: else insertedkv=false; - validateandupdate(array, true); // update data structure + /* update data structure */ + validateandupdate(array, true); return insertedkv; } @@ -156,8 +165,8 @@ search: private void validateandupdate(Slot[] newslots, boolean acceptupdatestolocal) { - //The cloud communication layer has checked slot HMACs already - //before decoding + /* The cloud communication layer has checked slot HMACs already + before decoding */ if (newslots.length==0) return; @@ -174,13 +183,13 @@ search: processSlot(indexer, slot, acceptupdatestolocal); } - //If there is a gap, check to see if the server sent us everything + /* If there is a gap, check to see if the server sent us everything. */ if (firstseqnum != (sequencenumber+1)) checkNumSlots(newslots.length); commitNewMaxSize(); - //commit new to slots + /* Commit new to slots. */ for(Slot slot:newslots) { buffer.putSlot(slot); } diff --git a/src/java/iotcloud/TableStatus.java b/src/java/iotcloud/TableStatus.java index fb50a7d..d77e9f2 100644 --- a/src/java/iotcloud/TableStatus.java +++ b/src/java/iotcloud/TableStatus.java @@ -1,6 +1,14 @@ package iotcloud; import java.nio.ByteBuffer; +/** + * TableStatus entries record the current size of the data structure + * in slots. Used to remember the size and to perform resizes. + * @author Brian Demsky + * @version 1.0 + */ + + class TableStatus extends Entry { private int maxslots; diff --git a/src/java/iotcloud/Test.java b/src/java/iotcloud/Test.java index dcd4f1e..c77702d 100644 --- a/src/java/iotcloud/Test.java +++ b/src/java/iotcloud/Test.java @@ -1,5 +1,11 @@ package iotcloud; +/** + * Test cases. + * @author Brian Demsky + * @version 1.0 + */ + public class Test { public static void main(String[] args) { if (args[0].equals("1")) @@ -15,17 +21,17 @@ public class Test { static Thread buildThread(String prefix, Table t) { return new Thread() { - public void run() { - for(int i=0; i<600; i++) { - String a=prefix+i; - IoTString ia=new IoTString(a); - t.put(ia, ia); - System.out.println(ia+"->"+t.get(ia)); - } - } + public void run() { + for(int i=0; i<600; i++) { + String a=prefix+i; + IoTString ia=new IoTString(a); + t.put(ia, ia); + System.out.println(ia+"->"+t.get(ia)); + } + } }; } - + static void test4() { Table t1=new Table("http://127.0.0.1/test.iotcloud/", "reallysecret", 321); Table t2=new Table("http://127.0.0.1/test.iotcloud/", "reallysecret", 351); @@ -42,7 +48,7 @@ public class Test { e.printStackTrace(); } } - + static void test3() { Table t1=new Table("http://127.0.0.1/test.iotcloud/", "reallysecret", 321); Table t2=new Table("http://127.0.0.1/test.iotcloud/", "reallysecret", 351); diff --git a/src/java/iotcloud/TestCloudComm.java b/src/java/iotcloud/TestCloudComm.java index b668237..9c752a8 100644 --- a/src/java/iotcloud/TestCloudComm.java +++ b/src/java/iotcloud/TestCloudComm.java @@ -1,8 +1,13 @@ package iotcloud; import java.io.*; import java.net.*; -import java.util.Arrays; -import javax.crypto.*; + +/** + * This class is a test driver to test the code w/o going through an + * actual web server. + * @author Brian Demsky + * @version 1.0 + */ class TestCloudComm extends CloudComm { SlotBuffer buffer; diff --git a/src/java/iotcloud/issues.txt b/src/java/iotcloud/issues.txt index aa63b73..da58ceb 100644 --- a/src/java/iotcloud/issues.txt +++ b/src/java/iotcloud/issues.txt @@ -1,4 +1,4 @@ 1) check expiration of rejectedmessage entries 2) check missing machine messages 3) add crypto -4) fix bugs with missing slots on server +4) handle Salt diff --git a/src/server/iotcloud.cpp b/src/server/iotcloud.cpp index e7eb2d3..bb9eff8 100644 --- a/src/server/iotcloud.cpp +++ b/src/server/iotcloud.cpp @@ -23,8 +23,10 @@ int main(void) { cin.rdbuf(&cin_fcgi_streambuf); cout.rdbuf(&cout_fcgi_streambuf); cerr.rdbuf(&cerr_fcgi_streambuf); + IoTQuery * iotquery=new IoTQuery(&request); iotquery->processQuery(); + delete iotquery; } diff --git a/src/server/iotquery.cpp b/src/server/iotquery.cpp index 8d708cb..117ca30 100644 --- a/src/server/iotquery.cpp +++ b/src/server/iotquery.cpp @@ -44,6 +44,10 @@ IoTQuery::~IoTQuery() { delete data; } +/** + * Returns true if the account directory exists. + */ + bool IoTQuery::checkDirectory() { struct stat s; int err=stat(directory, &s); @@ -52,6 +56,12 @@ bool IoTQuery::checkDirectory() { return S_ISDIR(s.st_mode); } +/** + * Decodes query string from client. Extracts type of request, + * sequence number, and whether the request changes the number of + * slots. + */ + void IoTQuery::decodeQuery() { int len=strlen(query); char * str=new char[len+1]; @@ -76,7 +86,7 @@ void IoTQuery::decodeQuery() { } } - //don't allow a really old sequence number + /* don't allow a really old sequence number */ if (requestsequencenumber < oldestentry) requestsequencenumber = oldestentry; @@ -91,6 +101,10 @@ void IoTQuery::decodeQuery() { delete str; } +/** + * Helper function to write data to file. + */ + void doWrite(int fd, char *data, long long length) { long long offset=0; do { @@ -108,6 +122,7 @@ void doWrite(int fd, char *data, long long length) { } while(length != 0); } +/** Helper function to read data from file. */ bool doRead(int fd, void *buf, int numbytes) { int offset=0; char *ptr=(char *)buf; @@ -122,6 +137,10 @@ bool doRead(int fd, void *buf, int numbytes) { return true; } +/** + * Function that handles a getSlot request. + */ + void IoTQuery::getSlot() { int numrequeststosend = (int)((newestentry-requestsequencenumber)+1); if (numrequeststosend < 0) @@ -144,7 +163,11 @@ void IoTQuery::getSlot() { delete filename; } const char header[]="getslot"; - long long size=sizeof(header)-1+sizeof(numrequeststosend)+4*numrequeststosend+numbytes; //header + payload + file count + sizes + + /* Size is the header + the payload + space for number of requests + plus sizes of each slot */ + + long long size=sizeof(header)-1+sizeof(numrequeststosend)+4*numrequeststosend+numbytes; char * response = new char[size]; long long offset=0; memcpy(response, header, sizeof(header)-1); @@ -158,7 +181,7 @@ void IoTQuery::getSlot() { offset+=sizeof(int); } - //copy file data + /* Read the file data into the buffer */ for(int i=0; i=0) { doRead(fdarray[i], response+offset, filesizes[i]); @@ -166,10 +189,10 @@ void IoTQuery::getSlot() { } } - //write response out + /* Send the response out to the webserver. */ sendResponse(response, size); - //cleanup + /* Delete the response buffer and close the files. */ delete response; for(int i=0; i= 0) @@ -177,30 +200,45 @@ void IoTQuery::getSlot() { } } +/** + * The method putSlot handles a putSlot request from the client + */ + void IoTQuery::putSlot() { + /* Check if the request is stale and send update in that case. This + servers as an implicit failure of the request. */ if (requestsequencenumber!=(newestentry+1)) { getSlot(); return; } + /* See if we have too many slots and if so, delete the old one */ int numberofliveslots=(int) ((newestentry-oldestentry)+1); if (numberofliveslots >= numqueueentries) { - //need to drop slot removeOldestSlot(); } - //write slot data out to file + /* Write the slot data we received to a SLOT file */ char *filename = getSlotFileName(requestsequencenumber); int slotfd = open(filename, O_CREAT|O_WRONLY, S_IRUSR| S_IWUSR); doWrite(slotfd, data, length); close(slotfd); delete filename; - newestentry = requestsequencenumber; // update sequence number - updateStatusFile(); // update counts + newestentry = requestsequencenumber; + + /* Update the seuqence numbers and other status file information. */ + updateStatusFile(); + + /* Send response acknowledging success */ char command[]="putslot"; sendResponse(command, sizeof(command)-1); } +/** + * Method sends response. It wraps in appropriate headers for web + * server. + */ + void IoTQuery::sendResponse(char * bytes, int len) { cout << "Accept-Ranges: bytes\r\n" << "Content-Length: " << len << "\r\n" @@ -208,13 +246,26 @@ void IoTQuery::sendResponse(char * bytes, int len) { cout.write(bytes, len); } -char * IoTQuery::getSlotFileName(long long slot) { +/** + * Computes the name for a slot file for the given sequence number. + */ + +char * IoTQuery::getSlotFileName(long long seqnum) { int directorylen=strlen(directory); - char * filename=new char[25+directorylen]; //19 digits for long number + 4 characters for SLOT + 1 character for null termination - snprintf(filename, 24+directorylen+1, "%s/SLOT%lld", directory, slot); + + /* Size is 19 digits for ASCII representation of a long + 4 + characters for SLOT string + 1 character for null termination + + directory size*/ + + char * filename=new char[25+directorylen]; + snprintf(filename, 24+directorylen+1, "%s/SLOT%lld", directory, seqnum); return filename; } +/** + * Removes the oldest slot file + */ + void IoTQuery::removeOldestSlot() { if (oldestentry!=0) { char * filename=getSlotFileName(oldestentry); @@ -224,31 +275,42 @@ void IoTQuery::removeOldestSlot() { oldestentry++; } +/** + * Processes the query sent to the fastcgi handler. + */ + void IoTQuery::processQuery() { getQuery(); getDirectory(); readData(); + /* Verify that we receive a post request. */ if (strncmp(method, "POST", 4) != 0) { cerr << "Not POST Request" << endl; return; } + /* Make sure the directory is okay. */ if (directory == NULL || !checkDirectory()) { cerr << "Directory " << directory << " does not exist" << endl; return; } + /* Get queue state from the status file. If it doesn't exist, + create it. */ if (!openStatusFile()) { cerr << "Failed to open status file" << endl; return; } + /* Lock status file to keep other requests out. */ flock(fd, LOCK_EX); + /* Decode query. */ decodeQuery(); + /* Handle request. */ if (reqGetSlot) getSlot(); else if (reqPutSlot) @@ -259,6 +321,11 @@ void IoTQuery::processQuery() { } } +/** + * Reads in data for request. This is used for the slot to be + * inserted. + */ + void IoTQuery::readData() { if (length) { data = new char[length+1]; @@ -271,12 +338,18 @@ void IoTQuery::readData() { } while (!cin.eof()); } + +/** + * Reads relevant environmental variables to find out the request. + */ + void IoTQuery::getQuery() { uri = FCGX_GetParam(uri_str, request->envp); query = FCGX_GetParam(query_str, request->envp); method = FCGX_GetParam(method_str, request->envp); iotcloudroot = FCGX_GetParam(iotcloudroot_str, request->envp); + /** We require the content-length header to be sent. */ char * reqlength = FCGX_GetParam(length_str, request->envp); if (reqlength) { length=strtoll(reqlength, NULL, 10); @@ -285,6 +358,10 @@ void IoTQuery::getQuery() { } } +/** + * Initializes directory field from environmental variables. + */ + void IoTQuery::getDirectory() { char * split = strchr((char *)uri, '?'); if (split == NULL) @@ -298,6 +375,10 @@ void IoTQuery::getDirectory() { directory[directory_len-1]=0; } +/** + * Helper function that is used to read the status file. + */ + int doread(int fd, void *ptr, size_t count, off_t offset) { do { size_t bytesread=pread(fd, ptr, count, offset); @@ -309,12 +390,22 @@ int doread(int fd, void *ptr, size_t count, off_t offset) { } while(1); } + +/** + * Writes the current state to the status file. + */ + void IoTQuery::updateStatusFile() { pwrite(fd, &numqueueentries, sizeof(numqueueentries), OFFSET_MAX); pwrite(fd, &oldestentry, sizeof(oldestentry), OFFSET_OLD); pwrite(fd, &newestentry, sizeof(newestentry), OFFSET_NEW); } +/** + * Reads in queue state from the status file. Returns true if + * successful. + */ + bool IoTQuery::openStatusFile() { char statusfile[]="queuestatus"; int len=strlen(directory); @@ -332,6 +423,7 @@ bool IoTQuery::openStatusFile() { return false; } + /* Read in queue size, oldest sequence number, and newest sequence number. */ int size; int needwrite=0; if (doread(fd, &size, sizeof(size), OFFSET_MAX)) diff --git a/src/server/iotquery.h b/src/server/iotquery.h index 05156b8..3394b57 100644 --- a/src/server/iotquery.h +++ b/src/server/iotquery.h @@ -31,18 +31,31 @@ private: FCGX_Request * request; char *data; + /* Directory slot files are placed in. */ char *directory; + /* Full URI from Apache */ const char * uri; + /* Query portion of URI */ const char * query; + /* Type of request: GET or PUT */ const char * method; + /* Root directory for all accounts */ const char * iotcloudroot; + /* Expected length of data from client */ long long length; + /* Sequence number for oldest slot */ long long oldestentry; + /* Sequence number for newest slot */ long long newestentry; + /* Sequence number from request */ long long requestsequencenumber; + /* Size of queue */ int numqueueentries; + /* fd for queuestatus file */ int fd; + /* Is the request to get a slot? */ bool reqGetSlot; + /* Is the request to put a slot? */ bool reqPutSlot; }; #endif