1/* Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6#include "handlers.h"
7
8#include <arpa/inet.h>
9#include <assert.h>
10#include <errno.h>
11#include <netdb.h>
12#include <netinet/in.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16
17#include <sys/types.h>
18#include <sys/socket.h>
19#include <sys/stat.h>
20
21#include "nacl_io/osdirent.h"
22
23#include "nacl_io_demo.h"
24
25#define MAX_OPEN_FILES 10
26#define MAX_OPEN_DIRS 10
27
28#if defined(WIN32)
29#define stat _stat
30#endif
31
32/**
33 * A mapping from int -> FILE*, so the JavaScript messages can refer to an open
34 * File. */
35static FILE* g_OpenFiles[MAX_OPEN_FILES];
36
37/**
38 * A mapping from int -> DIR*, so the JavaScript messages can refer to an open
39 * Directory. */
40static void* g_OpenDirs[MAX_OPEN_DIRS];
41
42/**
43 * Add |object| to |map| and return the index it was added at.
44 * @param[in] map The map to add the object to.
45 * @param[in] max_map_size The maximum map size.
46 * @param[in] object The object to add to the map.
47 * @return int The index of the added object, or -1 if there is no more space.
48 */
49static int AddToMap(void** map, int max_map_size, void* object) {
50  int i;
51  assert(object != NULL);
52  for (i = 0; i < max_map_size; ++i) {
53    if (map[i] == NULL) {
54      map[i] = object;
55      return i;
56    }
57  }
58
59  return -1;
60}
61
62/**
63 * Remove an object at index |i| from |map|.
64 * @param[in] map The map to remove from.
65 * @param[in] max_map_size The size of the map.
66 * @param[in] i The index to remove.
67 */
68static void RemoveFromMap(void** map, int max_map_size, int i) {
69  assert(i >= 0 && i < max_map_size);
70  map[i] = NULL;
71}
72
73/**
74 * Get the object from |map| at index |i|.
75 * @param[in] map The map to access.
76 * @param[in] max_map_size The size of the map.
77 * @param[in] i The index to access.
78 * @return the object at |map|. This will be NULL if there is no object at |i|.
79 */
80static void* GetFromMap(void** map, int max_map_size, int i) {
81  assert(i >= 0 && i < max_map_size);
82  return map[i];
83}
84
85/**
86 * Get an object given a string |s| containing the index.
87 * @param[in] map The map to access.
88 * @param[in] max_map_size The size of the map.
89 * @param[in] s The string containing the object index.
90 * @param[out] index The index of the object as an int.
91 * @return The object, or NULL if the index is invalid.
92 */
93static void* GetFromIndexString(void** map,
94                                int max_map_size,
95                                const char* s,
96                                int* index) {
97  char* endptr;
98  int result = strtol(s, &endptr, 10);
99  if (endptr != s + strlen(s)) {
100    /* Garbage at the end of the number...? */
101    return NULL;
102  }
103
104  if (index)
105    *index = result;
106
107  return GetFromMap(map, max_map_size, result);
108}
109
110/**
111 * Add the file to the g_OpenFiles map.
112 * @param[in] file The file to add to g_OpenFiles.
113 * @return int The index of the FILE in g_OpenFiles, or -1 if there are too many
114 *     open files. */
115static int AddFileToMap(FILE* file) {
116  return AddToMap((void**)g_OpenFiles, MAX_OPEN_FILES, file);
117}
118
119/**
120 * Remove the file from the g_OpenFiles map.
121 * @param[in] i The index of the file handle to remove. */
122static void RemoveFileFromMap(int i) {
123  RemoveFromMap((void**)g_OpenFiles, MAX_OPEN_FILES, i);
124}
125
126/**
127 * Get a file, given a string containing the index.
128 * @param[in] s The string containing the file index.
129 * @param[out] file_index The index of this file.
130 * @return The FILE* for this file, or NULL if the index is invalid.
131 */
132static FILE* GetFileFromIndexString(const char* s, int* file_index) {
133  return (FILE*)GetFromIndexString(
134      (void**)g_OpenFiles, MAX_OPEN_FILES, s, file_index);
135}
136
137/* Win32 doesn't support DIR/opendir/readdir/closedir. */
138#if !defined(WIN32)
139/**
140 * Add the dir to the g_OpenDirs map.
141 * @param[in] dir The dir to add to g_OpenDirs.
142 * @return int The index of the DIR in g_OpenDirs, or -1 if there are too many
143 *     open dirs. */
144static int AddDirToMap(DIR* dir) {
145  return AddToMap((void**)g_OpenDirs, MAX_OPEN_DIRS, dir);
146}
147
148/**
149 * Remove the dir from the g_OpenDirs map.
150 * @param[in] i The index of the dir handle to remove. */
151static void RemoveDirFromMap(int i) {
152  RemoveFromMap((void**)g_OpenDirs, MAX_OPEN_DIRS, i);
153}
154
155/**
156 * Get a dir, given a string containing the index.
157 * @param[in] s The string containing the dir index.
158 * @param[out] dir_index The index of this dir.
159 * @return The DIR* for this dir, or NULL if the index is invalid.
160 */
161static DIR* GetDirFromIndexString(const char* s, int* dir_index) {
162  return (DIR*)GetFromIndexString(
163      (void**)g_OpenDirs, MAX_OPEN_DIRS, s, dir_index);
164}
165#endif
166
167/**
168 * Handle a call to fopen() made by JavaScript.
169 *
170 * fopen expects 2 parameters:
171 *   0: the path of the file to open
172 *   1: the mode string
173 * on success, fopen returns a result in |output| separated by \1:
174 *   0: "fopen"
175 *   1: the filename opened
176 *   2: the file index
177 * on failure, fopen returns an error string in |output|.
178 *
179 * @param[in] num_params The number of params in |params|.
180 * @param[in] params An array of strings, parameters to this function.
181 * @param[out] output A string to write informational function output to.
182 * @return An errorcode; 0 means success, anything else is a failure. */
183int HandleFopen(int num_params, char** params, char** output) {
184  FILE* file;
185  int file_index;
186  const char* filename;
187  const char* mode;
188
189  if (num_params != 2) {
190    *output = PrintfToNewString("Error: fopen takes 2 parameters.");
191    return 1;
192  }
193
194  filename = params[0];
195  mode = params[1];
196
197  file = fopen(filename, mode);
198  if (!file) {
199    *output = PrintfToNewString("Error: fopen returned a NULL FILE*.");
200    return 2;
201  }
202
203  file_index = AddFileToMap(file);
204  if (file_index == -1) {
205    *output = PrintfToNewString(
206        "Error: Example only allows %d open file handles.", MAX_OPEN_FILES);
207    return 3;
208  }
209
210  *output = PrintfToNewString("fopen\1%s\1%d", filename, file_index);
211  return 0;
212}
213
214/**
215 * Handle a call to fwrite() made by JavaScript.
216 *
217 * fwrite expects 2 parameters:
218 *   0: The index of the file (which is mapped to a FILE*)
219 *   1: A string to write to the file
220 * on success, fwrite returns a result in |output| separated by \1:
221 *   0: "fwrite"
222 *   1: the file index
223 *   2: the number of bytes written
224 * on failure, fwrite returns an error string in |output|.
225 *
226 * @param[in] num_params The number of params in |params|.
227 * @param[in] params An array of strings, parameters to this function.
228 * @param[out] output A string to write informational function output to.
229 * @return An errorcode; 0 means success, anything else is a failure. */
230int HandleFwrite(int num_params, char** params, char** output) {
231  FILE* file;
232  const char* file_index_string;
233  const char* data;
234  size_t data_len;
235  size_t bytes_written;
236
237  if (num_params != 2) {
238    *output = PrintfToNewString("Error: fwrite takes 2 parameters.");
239    return 1;
240  }
241
242  file_index_string = params[0];
243  file = GetFileFromIndexString(file_index_string, NULL);
244  data = params[1];
245  data_len = strlen(data);
246
247  if (!file) {
248    *output =
249        PrintfToNewString("Error: Unknown file handle %s.", file_index_string);
250    return 2;
251  }
252
253  bytes_written = fwrite(data, 1, data_len, file);
254
255  if (ferror(file)) {
256    *output = PrintfToNewString(
257        "Error: Wrote %d bytes, but ferror() returns true.", bytes_written);
258    return 3;
259  }
260
261  *output =
262      PrintfToNewString("fwrite\1%s\1%d", file_index_string, bytes_written);
263  return 0;
264}
265
266/**
267 * Handle a call to fread() made by JavaScript.
268 *
269 * fread expects 2 parameters:
270 *   0: The index of the file (which is mapped to a FILE*)
271 *   1: The number of bytes to read from the file.
272 * on success, fread returns a result in |output| separated by \1:
273 *   0: "fread"
274 *   1: the file index
275 *   2: the data read from the file
276 * on failure, fread returns an error string in |output|.
277 *
278 * @param[in] num_params The number of params in |params|.
279 * @param[in] params An array of strings, parameters to this function.
280 * @param[out] output A string to write informational function output to.
281 * @return An errorcode; 0 means success, anything else is a failure. */
282int HandleFread(int num_params, char** params, char** output) {
283  FILE* file;
284  const char* file_index_string;
285  char* buffer;
286  size_t data_len;
287  size_t bytes_read;
288
289  if (num_params != 2) {
290    *output = PrintfToNewString("Error: fread takes 2 parameters.");
291    return 1;
292  }
293
294  file_index_string = params[0];
295  file = GetFileFromIndexString(file_index_string, NULL);
296  data_len = strtol(params[1], NULL, 10);
297
298  if (!file) {
299    *output =
300        PrintfToNewString("Error: Unknown file handle %s.", file_index_string);
301    return 2;
302  }
303
304  buffer = (char*)malloc(data_len + 1);
305  bytes_read = fread(buffer, 1, data_len, file);
306  buffer[bytes_read] = 0;
307
308  if (ferror(file)) {
309    *output = PrintfToNewString(
310        "Error: Read %d bytes, but ferror() returns true.", bytes_read);
311    return 3;
312  }
313
314  *output = PrintfToNewString("fread\1%s\1%s", file_index_string, buffer);
315  free(buffer);
316  return 0;
317}
318
319/**
320 * Handle a call to fseek() made by JavaScript.
321 *
322 * fseek expects 3 parameters:
323 *   0: The index of the file (which is mapped to a FILE*)
324 *   1: The offset to seek to
325 *   2: An integer representing the whence parameter of standard fseek.
326 *      whence = 0: seek from the beginning of the file
327 *      whence = 1: seek from the current file position
328 *      whence = 2: seek from the end of the file
329 * on success, fseek returns a result in |output| separated by \1:
330 *   0: "fseek"
331 *   1: the file index
332 *   2: The new file position
333 * on failure, fseek returns an error string in |output|.
334 *
335 * @param[in] num_params The number of params in |params|.
336 * @param[in] params An array of strings, parameters to this function.
337 * @param[out] output A string to write informational function output to.
338 * @return An errorcode; 0 means success, anything else is a failure. */
339int HandleFseek(int num_params, char** params, char** output) {
340  FILE* file;
341  const char* file_index_string;
342  long offset;
343  int whence;
344  int result;
345
346  if (num_params != 3) {
347    *output = PrintfToNewString("Error: fseek takes 3 parameters.");
348    return 1;
349  }
350
351  file_index_string = params[0];
352  file = GetFileFromIndexString(file_index_string, NULL);
353  offset = strtol(params[1], NULL, 10);
354  whence = strtol(params[2], NULL, 10);
355
356  if (!file) {
357    *output =
358        PrintfToNewString("Error: Unknown file handle %s.", file_index_string);
359    return 2;
360  }
361
362  result = fseek(file, offset, whence);
363  if (result) {
364    *output = PrintfToNewString("Error: fseek returned error %d.", result);
365    return 3;
366  }
367
368  offset = ftell(file);
369  if (offset < 0) {
370    *output = PrintfToNewString(
371        "Error: fseek succeeded, but ftell returned error %d.", offset);
372    return 4;
373  }
374
375  *output = PrintfToNewString("fseek\1%s\1%d", file_index_string, offset);
376  return 0;
377}
378
379/**
380 * Handle a call to fclose() made by JavaScript.
381 *
382 * fclose expects 1 parameter:
383 *   0: The index of the file (which is mapped to a FILE*)
384 * on success, fclose returns a result in |output| separated by \1:
385 *   0: "fclose"
386 *   1: the file index
387 * on failure, fclose returns an error string in |output|.
388 *
389 * @param[in] num_params The number of params in |params|.
390 * @param[in] params An array of strings, parameters to this function.
391 * @param[out] output A string to write informational function output to.
392 * @return An errorcode; 0 means success, anything else is a failure. */
393int HandleFclose(int num_params, char** params, char** output) {
394  FILE* file;
395  int file_index;
396  const char* file_index_string;
397  int result;
398
399  if (num_params != 1) {
400    *output = PrintfToNewString("Error: fclose takes 1 parameters.");
401    return 1;
402  }
403
404  file_index_string = params[0];
405  file = GetFileFromIndexString(file_index_string, &file_index);
406  if (!file) {
407    *output =
408        PrintfToNewString("Error: Unknown file handle %s.", file_index_string);
409    return 2;
410  }
411
412  result = fclose(file);
413  if (result) {
414    *output = PrintfToNewString("Error: fclose returned error %d.", result);
415    return 3;
416  }
417
418  RemoveFileFromMap(file_index);
419
420  *output = PrintfToNewString("fclose\1%s", file_index_string);
421  return 0;
422}
423
424/**
425 * Handle a call to stat() made by JavaScript.
426 *
427 * stat expects 1 parameter:
428 *   0: The name of the file
429 * on success, stat returns a result in |output| separated by \1:
430 *   0: "stat"
431 *   1: the file name
432 *   2: the size of the file
433 * on failure, stat returns an error string in |output|.
434 *
435 * @param[in] num_params The number of params in |params|.
436 * @param[in] params An array of strings, parameters to this function.
437 * @param[out] output A string to write informational function output to.
438 * @return An errorcode; 0 means success, anything else is a failure. */
439int HandleStat(int num_params, char** params, char** output) {
440  const char* filename;
441  int result;
442  struct stat buf;
443
444  if (num_params != 1) {
445    *output = PrintfToNewString("Error: stat takes 1 parameter.");
446    return 1;
447  }
448
449  filename = params[0];
450
451  memset(&buf, 0, sizeof(buf));
452  result = stat(filename, &buf);
453  if (result == -1) {
454    *output = PrintfToNewString("Error: stat returned error %d.", errno);
455    return 2;
456  }
457
458  *output = PrintfToNewString("stat\1%s\1%d", filename, buf.st_size);
459  return 0;
460}
461
462/**
463 * Handle a call to opendir() made by JavaScript.
464 *
465 * opendir expects 1 parameter:
466 *   0: The name of the directory
467 * on success, opendir returns a result in |output| separated by \1:
468 *   0: "opendir"
469 *   1: the directory name
470 *   2: the index of the directory
471 * on failure, opendir returns an error string in |output|.
472 *
473 * @param[in] num_params The number of params in |params|.
474 * @param[in] params An array of strings, parameters to this function.
475 * @param[out] output A string to write informational function output to.
476 * @return An errorcode; 0 means success, anything else is a failure. */
477int HandleOpendir(int num_params, char** params, char** output) {
478#if defined(WIN32)
479  *output = PrintfToNewString("Error: Win32 does not support opendir.");
480  return 1;
481#else
482  DIR* dir;
483  int dir_index;
484  const char* dirname;
485
486  if (num_params != 1) {
487    *output = PrintfToNewString("Error: opendir takes 1 parameter.");
488    return 1;
489  }
490
491  dirname = params[0];
492
493  dir = opendir(dirname);
494  if (!dir) {
495    *output = PrintfToNewString("Error: opendir returned a NULL DIR*.");
496    return 2;
497  }
498
499  dir_index = AddDirToMap(dir);
500  if (dir_index == -1) {
501    *output = PrintfToNewString(
502        "Error: Example only allows %d open dir handles.", MAX_OPEN_DIRS);
503    return 3;
504  }
505
506  *output = PrintfToNewString("opendir\1%s\1%d", dirname, dir_index);
507  return 0;
508#endif
509}
510
511/**
512 * Handle a call to readdir() made by JavaScript.
513 *
514 * readdir expects 1 parameter:
515 *   0: The index of the directory (which is mapped to a DIR*)
516 * on success, opendir returns a result in |output| separated by \1:
517 *   0: "readdir"
518 *   1: the inode number of the entry
519 *   2: the name of the entry
520 * on failure, readdir returns an error string in |output|.
521 *
522 * @param[in] num_params The number of params in |params|.
523 * @param[in] params An array of strings, parameters to this function.
524 * @param[out] output A string to write informational function output to.
525 * @return An errorcode; 0 means success, anything else is a failure. */
526int HandleReaddir(int num_params, char** params, char** output) {
527#if defined(WIN32)
528  *output = PrintfToNewString("Error: Win32 does not support readdir.");
529  return 1;
530#else
531  DIR* dir;
532  const char* dir_index_string;
533  struct dirent* entry;
534
535  if (num_params != 1) {
536    *output = PrintfToNewString("Error: readdir takes 1 parameter.");
537    return 1;
538  }
539
540  dir_index_string = params[0];
541  dir = GetDirFromIndexString(dir_index_string, NULL);
542
543  if (!dir) {
544    *output = PrintfToNewString("Error: Unknown dir handle %s.",
545                                dir_index_string);
546    return 2;
547  }
548
549  entry = readdir(dir);
550  if (entry != NULL) {
551    *output = PrintfToNewString("readdir\1%s\1%d\1%s", dir_index_string,
552                                entry->d_ino, entry->d_name);
553  } else {
554    *output = PrintfToNewString("readdir\1%s\1\1", dir_index_string);
555  }
556
557  return 0;
558#endif
559}
560
561/**
562 * Handle a call to closedir() made by JavaScript.
563 *
564 * closedir expects 1 parameter:
565 *   0: The index of the directory (which is mapped to a DIR*)
566 * on success, closedir returns a result in |output| separated by \1:
567 *   0: "closedir"
568 *   1: the name of the directory
569 * on failure, closedir returns an error string in |output|.
570 *
571 * @param[in] num_params The number of params in |params|.
572 * @param[in] params An array of strings, parameters to this function.
573 * @param[out] output A string to write informational function output to.
574 * @return An errorcode; 0 means success, anything else is a failure. */
575int HandleClosedir(int num_params, char** params, char** output) {
576#if defined(WIN32)
577  *output = PrintfToNewString("Error: Win32 does not support closedir.");
578  return 1;
579#else
580  DIR* dir;
581  int dir_index;
582  const char* dir_index_string;
583  int result;
584
585  if (num_params != 1) {
586    *output = PrintfToNewString("Error: closedir takes 1 parameters.");
587    return 1;
588  }
589
590  dir_index_string = params[0];
591  dir = GetDirFromIndexString(dir_index_string, &dir_index);
592  if (!dir) {
593    *output = PrintfToNewString("Error: Unknown dir handle %s.",
594                                dir_index_string);
595    return 2;
596  }
597
598  result = closedir(dir);
599  if (result) {
600    *output = PrintfToNewString("Error: closedir returned error %d.", result);
601    return 3;
602  }
603
604  RemoveDirFromMap(dir_index);
605
606  *output = PrintfToNewString("closedir\1%s", dir_index_string);
607  return 0;
608#endif
609}
610
611/**
612 * Handle a call to mkdir() made by JavaScript.
613 *
614 * mkdir expects 1 parameter:
615 *   0: The name of the directory
616 *   1: The mode to use for the new directory, in octal.
617 * on success, mkdir returns a result in |output| separated by \1:
618 *   0: "mkdir"
619 *   1: the name of the directory
620 * on failure, mkdir returns an error string in |output|.
621 *
622 * @param[in] num_params The number of params in |params|.
623 * @param[in] params An array of strings, parameters to this function.
624 * @param[out] output A string to write informational function output to.
625 * @return An errorcode; 0 means success, anything else is a failure. */
626int HandleMkdir(int num_params, char** params, char** output) {
627  const char* dirname;
628  int result;
629  int mode;
630
631  if (num_params != 2) {
632    *output = PrintfToNewString("Error: mkdir takes 2 parameters.");
633    return 1;
634  }
635
636  dirname = params[0];
637  mode = strtol(params[1], NULL, 8);
638
639  result = mkdir(dirname, mode);
640  if (result != 0) {
641    *output = PrintfToNewString("Error: mkdir returned error: %d", errno);
642    return 2;
643  }
644
645  *output = PrintfToNewString("mkdir\1%s", dirname);
646  return 0;
647}
648
649/**
650 * Handle a call to gethostbyname() made by JavaScript.
651 *
652 * gethostbyname expects 1 parameter:
653 *   0: The name of the host to look up.
654 * on success, gethostbyname returns a result in |output| separated by \1:
655 *   0: "gethostbyname"
656 *   1: Host name
657 *   2: Address type (either "AF_INET" or "AF_INET6")
658 *   3. The first address.
659 *   4+ The second, third, etc. addresses.
660 * on failure, gethostbyname returns an error string in |output|.
661 *
662 * @param[in] num_params The number of params in |params|.
663 * @param[in] params An array of strings, parameters to this function.
664 * @param[out] output A string to write informational function output to.
665 * @return An errorcode; 0 means success, anything else is a failure. */
666int HandleGethostbyname(int num_params, char** params, char** output) {
667  struct hostent* info;
668  struct in_addr **addr_list;
669  const char* addr_type;
670  const char* name;
671  char inet6_addr_str[INET6_ADDRSTRLEN];
672  int non_variable_len, output_len;
673  int current_pos;
674  int i;
675
676  if (num_params != 1) {
677    *output = PrintfToNewString("Error: gethostbyname takes 1 parameter.");
678    return 1;
679  }
680
681  name = params[0];
682
683  info = gethostbyname(name);
684  if (!info) {
685    *output = PrintfToNewString("Error: gethostbyname failed, error is \"%s\"",
686                                hstrerror(h_errno));
687    return 2;
688  }
689
690  addr_type = info->h_addrtype == AF_INET ? "AF_INET" : "AF_INET6";
691
692  non_variable_len = strlen("gethostbyname") + 1
693    + strlen(info->h_name) + 1 + strlen(addr_type);
694  output_len = non_variable_len;
695
696  addr_list = (struct in_addr **)info->h_addr_list;
697  for (i = 0; addr_list[i] != NULL; i++) {
698    output_len += 1; // for the divider
699    if (info->h_addrtype == AF_INET) {
700      output_len += strlen(inet_ntoa(*addr_list[i]));
701    } else { // IPv6
702      inet_ntop(AF_INET6, addr_list[i], inet6_addr_str, INET6_ADDRSTRLEN);
703      output_len += strlen(inet6_addr_str);
704    }
705  }
706
707  *output = (char*) calloc(output_len + 1, 1);
708  if (!*output) {
709    *output = PrintfToNewString("Error: out of memory.");
710    return 3;
711  }
712  snprintf(*output, non_variable_len + 1, "gethostbyname\1%s\1%s",
713           info->h_name, addr_type);
714
715  current_pos = non_variable_len;
716  for (i = 0; addr_list[i] != NULL; i++) {
717    if (info->h_addrtype == AF_INET) {
718      current_pos += sprintf(*output + current_pos,
719                             "\1%s", inet_ntoa(*addr_list[i]));
720    } else { // IPv6
721      inet_ntop(AF_INET6, addr_list[i], inet6_addr_str, INET6_ADDRSTRLEN);
722      sprintf(*output + current_pos, "\1%s", inet6_addr_str);
723    }
724  }
725  return 0;
726}
727