Compiles
[iotcloud.git] / version2 / src / server_malicious_override_do_rej / iotquery.cpp
1 #include "iotquery.h"
2 #include <string.h>
3 #include <sys/stat.h>
4 #include <sys/types.h>
5 #include <sys/file.h>
6 #include <fcntl.h>
7 #include <unistd.h>
8 #include <stdlib.h>
9 #include <errno.h>
10 #include <netinet/in.h>
11
12 using namespace std;
13
14 const char * query_str = "QUERY_STRING";
15 const char * uri_str = "REQUEST_URI";
16 const char * method_str = "REQUEST_METHOD";
17 const char * iotcloudroot_str = "IOTCLOUD_ROOT";
18 const char * length_str = "CONTENT_LENGTH";
19
20 IoTQuery::IoTQuery(FCGX_Request *request) :
21         request(request),
22         data(NULL),
23         directory(NULL),
24         uri(NULL),
25         query(NULL),
26         method(NULL),
27         iotcloudroot(NULL),
28         length(0),
29         oldestentry(0),
30         newestentry(0),
31         requestsequencenumber(0),
32         numqueueentries(DEFAULT_SIZE),
33         fd(-1),
34         reqGetSlot(false),
35         reqPutSlot(false),
36         reqSetSalt(false),
37         reqGetSalt(false) {
38 }
39
40 IoTQuery::~IoTQuery() {
41         if (fd >= 0)
42                 close(fd);
43         if (directory)
44                 delete directory;
45         if (data)
46                 delete data;
47 }
48
49 /**
50  *  Returns true if the account directory exists.
51  */
52
53 bool IoTQuery::checkDirectory() {
54         struct stat s;
55         int err = stat(directory, &s);
56         if (-1 == err)
57                 return false;
58         return S_ISDIR(s.st_mode);
59 }
60
61 /**
62  * Decodes query string from client. Extracts type of request,
63  * sequence number, and whether the request changes the number of
64  * slots.
65  */
66
67 void IoTQuery::decodeQuery() {
68         int len = strlen(query);
69         char * str = new char[len + 1];
70         memcpy(str, query, len + 1);
71         char *tok_ptr = str;
72
73         /* Parse commands */
74         char *command = strsep(&tok_ptr, "&");
75         if (strncmp(command, "req=putslot", 11) == 0)
76                 reqPutSlot = true;
77         else if (strncmp(command, "req=getslot", 11) == 0)
78                 reqGetSlot = true;
79         else if (strncmp(command, "req=setsalt", 11) == 0)
80                 reqSetSalt = true;
81         else if (strncmp(command, "req=getsalt", 11) == 0)
82                 reqGetSalt = true;
83
84         /* Load Sequence Number for request */
85         char *sequencenumber_str = strsep(&tok_ptr, "&");
86         if (sequencenumber_str != NULL &&
87                 strncmp(sequencenumber_str, "seq=", 4) == 0) {
88                 sequencenumber_str = strchr(sequencenumber_str, '=');
89                 if (sequencenumber_str != NULL) {
90                         requestsequencenumber = strtoll(sequencenumber_str + 1, NULL, 10);
91                 }
92         }
93
94         /* don't allow a really old sequence number */
95         if (requestsequencenumber < oldestentry)
96                 requestsequencenumber = oldestentry;
97
98         /* Update size if we get request */
99         char * numqueueentries_str = tok_ptr;
100         if (numqueueentries_str != NULL &&
101                 strncmp(numqueueentries_str, "max=", 4) == 0) {
102                 numqueueentries_str = strchr(numqueueentries_str, '=') + 1;
103                 numqueueentries = strtoll(numqueueentries_str, NULL, 10);
104         }
105
106         delete str;
107 }
108
109 /**
110  * Helper function to write data to file.
111  */
112
113 void doWrite(int fd, char *data, long long length) {
114         long long offset = 0;
115         do {
116                 long long byteswritten = write(fd, &data[offset], length);
117                 if (byteswritten > 0) {
118                         length -= byteswritten;
119                         offset += byteswritten;
120                 } else {
121                         cerr << "Bytes not written" << endl;
122                         if (byteswritten < 0) {
123                                 cerr << strerror(errno) << " error writing slot file" << endl;
124                         }
125                         return;
126                 }
127         } while (length != 0);
128 }
129
130 /** Helper function to read data from file. */
131 bool doRead(int fd, void *buf, int numbytes) {
132         int offset = 0;
133         char *ptr = (char *)buf;
134         do {
135                 int bytesread = read(fd, ptr + offset, numbytes);
136                 if (bytesread > 0) {
137                         offset += bytesread;
138                         numbytes -= bytesread;
139                 } else
140                         return false;
141         } while (numbytes != 0);
142         return true;
143 }
144
145 /**
146  * Function that handles a getSlot request.
147  */
148
149 void IoTQuery::getSlot() {
150         int numrequeststosend = (int)((newestentry - requestsequencenumber) + 1);
151         if (numrequeststosend < 0)
152                 numrequeststosend = 0;
153         long long numbytes = 0;
154         int filesizes[numrequeststosend];
155         int fdarray[numrequeststosend];
156         int index = 0;
157         for (long long seqn = requestsequencenumber; seqn <= newestentry; seqn++, index++) {
158                 struct stat st;
159                 char *filename = getSlotFileName(seqn);
160                 if (stat(filename, &st) == 0) {
161                         fdarray[index] = open(filename, O_RDONLY);
162                         filesizes[index] = st.st_size;
163                         numbytes += filesizes[index];
164                 } else {
165                         fdarray[index] = -1;
166                         filesizes[index] = 0;
167                 }
168                 delete filename;
169         }
170         const char header[] = "getslot";
171
172         /* Size is the header + the payload + space for number of requests
173                  plus sizes of each slot */
174
175         long long size = sizeof(header) - 1 + sizeof(numrequeststosend) + 4 * numrequeststosend + numbytes;
176         char * response = new char[size];
177         long long offset = 0;
178         memcpy(response, header, sizeof(header) - 1);
179         offset += sizeof(header) - 1;
180         int numreq = htonl(numrequeststosend);
181         memcpy(response + offset, &numreq, sizeof(numreq));
182         offset += sizeof(numrequeststosend);
183         for (int i = 0; i < numrequeststosend; i++) {
184                 int filesize = htonl(filesizes[i]);
185                 memcpy(response + offset, &filesize, sizeof(filesize));
186                 offset += sizeof(int);
187         }
188
189         /* Read the file data into the buffer */
190         for (int i = 0; i < numrequeststosend; i++) {
191                 if (fdarray[i] >= 0) {
192                         doRead(fdarray[i], response + offset, filesizes[i]);
193                         offset += filesizes[i];
194                 }
195         }
196
197         /* Send the response out to the webserver. */
198         sendResponse(response, size);
199
200         /* Delete the response buffer and close the files. */
201         delete response;
202         for (int i = 0; i < numrequeststosend; i++) {
203                 if (fdarray[i] >= 0)
204                         close(fdarray[i]);
205         }
206 }
207
208 /**
209  * The method setSalt handles a setSalt request from the client.
210  */
211 void IoTQuery::setSalt() {
212         /* Write the slot data we received to a SLOT file */
213         char *filename = getSaltFileName();
214         int saltfd = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
215         doWrite(saltfd, data, length);
216         char response[0];
217         sendResponse(response, 0);
218         close(saltfd);
219         delete filename;
220 }
221
222 /**
223  * The method getSalt handles a getSalt request from the client.
224  */
225
226 void IoTQuery::getSalt() {
227         /* Write the slot data we received to a SLOT file */
228         char *filename = getSaltFileName();
229         int filesize = 0;
230         struct stat st;
231         if (stat(filename, &st) == 0) {
232                 filesize = st.st_size;
233         } else {
234                 char response[0];
235                 sendResponse(response, 0);
236                 delete filename;
237                 return;
238         }
239         int saltfd = open(filename, O_RDONLY);
240         int responsesize = filesize + sizeof(int);
241         char * response = new char[responsesize];
242         doRead(saltfd, response + sizeof(int), filesize);
243         int n_filesize = htonl(filesize);
244         *((int*) response) = n_filesize;
245         sendResponse(response, responsesize);
246         close(saltfd);
247         delete filename;
248         delete response;
249 }
250
251 /**
252  *      The method putSlot handles a putSlot request from the client
253  */
254
255 void IoTQuery::putSlot() {
256         /* Check if the request is stale and send update in that case.  This
257                  servers as an implicit failure of the request. */
258         if (requestsequencenumber == 150)
259         {
260                 if (requestsequencenumber != (newestentry + 1)) {
261
262                         /* Write the slot data we received to a SLOT file */
263                         char *filename = getSlotFileName(requestsequencenumber);
264                         unlink(filename);
265                         int slotfd = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
266                         doWrite(slotfd, data, length);
267                         close(slotfd);
268                         delete filename;
269
270                         getSlot();
271                         return;
272                 }
273         }
274
275
276
277         if (requestsequencenumber != (newestentry + 1)) {
278                 getSlot();
279                 return;
280         }
281
282         /* See if we have too many slots and if so, delete the old one */
283         int numberofliveslots = (int) ((newestentry - oldestentry) + 1);
284         if (numberofliveslots >=  numqueueentries) {
285                 removeOldestSlot();
286         }
287
288         /* Write the slot data we received to a SLOT file */
289         char *filename = getSlotFileName(requestsequencenumber);
290         int slotfd = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
291         doWrite(slotfd, data, length);
292         close(slotfd);
293         delete filename;
294         newestentry = requestsequencenumber;
295
296         /* Update the seuqence numbers and other status file information. */
297         updateStatusFile();
298
299         /* Send response acknowledging success */
300         char command[] = "putslot";
301         sendResponse(command, sizeof(command) - 1);
302 }
303
304 /**
305  * Method sends response.  It wraps in appropriate headers for web
306  * server.
307  */
308
309 void IoTQuery::sendResponse(char * bytes, int len) {
310         cout << "Accept-Ranges: bytes\r\n"
311              << "Content-Length: " << len << "\r\n"
312              << "\r\n";
313         cout.write(bytes, len);
314         cout << flush;
315 }
316
317 /**
318  *      Computes the name for a slot file for the given sequence number.
319  */
320
321 char * IoTQuery::getSlotFileName(long long seqnum) {
322         int directorylen = strlen(directory);
323
324         /* Size is 19 digits for ASCII representation of a long + 4
325                  characters for SLOT string + 1 character for null termination +
326                  directory size*/
327
328         char * filename = new char[25 + directorylen];
329         snprintf(filename, 25 + directorylen, "%s/SLOT%lld", directory, seqnum);
330         return filename;
331 }
332
333 /**
334  *      Computes the name for a salt file
335  */
336
337 char * IoTQuery::getSaltFileName() {
338         int directorylen = strlen(directory);
339
340         /* Size is 4 characters for SALT string + 1 character for null
341                  termination + directory size*/
342
343         char * filename = new char[6 + directorylen];
344         snprintf(filename, 6 + directorylen, "%s/SALT", directory);
345         return filename;
346 }
347
348 /**
349  *  Removes the oldest slot file
350  */
351
352 void IoTQuery::removeOldestSlot() {
353         if (oldestentry != 0) {
354                 char * filename = getSlotFileName(oldestentry);
355                 unlink(filename);
356                 delete filename;
357         }
358         oldestentry++;
359 }
360
361 /**
362  * Processes the query sent to the fastcgi handler.
363  */
364
365 void IoTQuery::processQuery() {
366         getQuery();
367         getDirectory();
368         // readData();
369         if (!readData())
370         {
371                 cerr << "No Data Available" << endl;
372                 return;
373         }
374
375
376         /* Verify that we receive a post request. */
377         if (strncmp(method, "POST", 4) != 0) {
378                 cerr << "Not POST Request" << endl;
379                 return;
380         }
381
382         /* Make sure the directory is okay. */
383         if (directory == NULL ||
384                 !checkDirectory()) {
385                 cerr << "Directory " << directory << " does not exist" << endl;
386                 return;
387         }
388
389         /* Get queue state from the status file.  If it doesn't exist,
390                  create it. */
391         if (!openStatusFile()) {
392                 cerr << "Failed to open status file" << endl;
393                 return;
394         }
395
396         /* Lock status file to keep other requests out. */
397         flock(fd, LOCK_EX);
398
399         /* Decode query. */
400         decodeQuery();
401
402         /* Handle request. */
403         if (reqGetSlot)
404                 getSlot();
405         else if (reqPutSlot)
406                 putSlot();
407         else if (reqSetSalt)
408                 setSalt();
409         else if (reqGetSalt)
410                 getSalt();
411         else {
412                 cerr << "No recognized request" << endl;
413                 return;
414         }
415 }
416
417 /**
418  * Reads in data for request.  This is used for the slot to be
419  * inserted.
420  */
421
422 bool IoTQuery::readData() {
423         if (length != 0) {
424                 data = new char[length + 1];
425                 memset(data, 0, length + 1);
426                 cin.read(data, length);
427         }
428
429         do {
430                 char dummy;
431                 cin >> dummy;
432         } while (!cin.eof());
433
434         if (length != 0)
435         {
436                 if (cin.gcount() != length)
437                 {
438                         return false;
439                 }
440         }
441
442         return true;
443 }
444
445
446 /**
447  * Reads relevant environmental variables to find out the request.
448  */
449
450 void IoTQuery::getQuery() {
451         uri = FCGX_GetParam(uri_str, request->envp);
452         query = FCGX_GetParam(query_str, request->envp);
453         method = FCGX_GetParam(method_str, request->envp);
454         iotcloudroot = FCGX_GetParam(iotcloudroot_str, request->envp);
455
456         /** We require the content-length header to be sent. */
457         char * reqlength = FCGX_GetParam(length_str, request->envp);
458         if (reqlength) {
459                 length = strtoll(reqlength, NULL, 10);
460         } else {
461                 length = 0;
462         }
463 }
464
465 /**
466  *  Initializes directory field from environmental variables.
467  */
468
469 void IoTQuery::getDirectory() {
470         char * split = strchr((char *)uri, '?');
471         if (split == NULL)
472                 return;
473         int split_len = (int) (split - uri);
474         int rootdir_len = strlen(iotcloudroot);
475         int directory_len = split_len + rootdir_len + 1;
476         directory = new char[directory_len];
477         memcpy(directory, iotcloudroot, rootdir_len);
478         memcpy(directory + rootdir_len, uri, split_len);
479         directory[directory_len - 1] = 0;
480 }
481
482 /**
483  * Helper function that is used to read the status file.
484  */
485
486 int doread(int fd, void *ptr, size_t count, off_t offset) {
487         do {
488                 size_t bytesread = pread(fd, ptr, count, offset);
489                 if (bytesread == count) {
490                         return 1;
491                 } else if (bytesread == 0) {
492                         return 0;
493                 }
494         } while (1);
495 }
496
497
498 /**
499  * Writes the current state to the status file.
500  */
501
502 void IoTQuery::updateStatusFile() {
503         pwrite(fd, &numqueueentries, sizeof(numqueueentries), OFFSET_MAX);
504         pwrite(fd, &oldestentry, sizeof(oldestentry), OFFSET_OLD);
505         pwrite(fd, &newestentry, sizeof(newestentry), OFFSET_NEW);
506 }
507
508 /**
509  * Reads in queue state from the status file.  Returns true if
510  * successful.
511  */
512
513 bool IoTQuery::openStatusFile() {
514         char statusfile[] = "queuestatus";
515         int len = strlen(directory);
516
517         char * filename = new char[len + sizeof(statusfile) + 2];
518         memcpy(filename, directory, len);
519         filename[len] = '/';
520         memcpy(filename + len + 1, statusfile, sizeof(statusfile));
521         filename[len + sizeof(statusfile) + 1] = 0;
522         fd = open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
523         delete filename;
524
525         if (fd < 0) {
526                 cerr << strerror(errno) << " error opening statusfile" << endl;
527                 return false;
528         }
529
530         /* Read in queue size, oldest sequence number, and newest sequence number. */
531         int size;
532         int needwrite = 0;
533         if (doread(fd, &size, sizeof(size), OFFSET_MAX))
534                 numqueueentries = size;
535         else
536                 needwrite = 1;
537
538         long long entry;
539         if (doread(fd, &entry, sizeof(entry), OFFSET_OLD))
540                 oldestentry = entry;
541         else
542                 needwrite = 1;
543
544         if (doread(fd, &entry, sizeof(entry), OFFSET_NEW))
545                 newestentry = entry;
546         else
547                 needwrite = 1;
548
549         if (needwrite)
550                 updateStatusFile();
551
552         return true;
553 }
554
555