Home | History | Annotate | Download | only in nacl_io
      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. */
     35 static 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. */
     40 static 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  */
     49 static 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  */
     68 static 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  */
     80 static 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  */
     93 static 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. */
    115 static 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. */
    122 static 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  */
    132 static 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. */
    144 static 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. */
    151 static 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  */
    161 static 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. */
    183 int 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. */
    230 int 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. */
    282 int 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. */
    339 int 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. */
    393 int 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. */
    439 int 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. */
    477 int 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. */
    526 int 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. */
    575 int 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. */
    626 int 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. */
    666 int 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