LCOV - code coverage report
Current view: top level - src/plugins/hosts - hosts-get.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 130 136 95.6 %
Date: 2019-09-12 12:28:41 Functions: 10 11 90.9 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Contains the get direction of the hosts plugin
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include "hosts.h"
      11             : #include "keymetaformatting.h"
      12             : 
      13             : #include <errno.h>
      14             : #include <netdb.h>
      15             : #include <sys/socket.h>
      16             : #include <sys/types.h>
      17             : 
      18             : #ifndef HAVE_KDBCONFIG
      19             : #include "kdbconfig.h"
      20             : #endif
      21             : 
      22             : #include <kdbproposal.h>
      23             : 
      24             : #define MAX_SPACE_BUFFER = 16;
      25             : 
      26             : typedef void CommentConstructor (KeySet *, size_t, const char *, const char *);
      27             : 
      28             : /*
      29             :  * Determines the address family of the supplied network address
      30             :  *
      31             :  * @param address the network address to be analyzed
      32             :  * @return a number identifying the network address (e.g. AF_INET) or -1 if an error occurred
      33             :  */
      34          55 : static int getAddressFamily (const char * address)
      35             : {
      36             :         struct addrinfo hint;
      37             :         struct addrinfo * info;
      38          55 :         memset (&hint, 0, sizeof (hint));
      39             : 
      40             :         /* no specific family is requested and name lookups are disabled */
      41             :         hint.ai_family = AF_UNSPEC;
      42          55 :         hint.ai_flags = AI_NUMERICHOST;
      43             : 
      44          55 :         int ret = getaddrinfo (address, 0, &hint, &info);
      45             : 
      46          55 :         if (ret != 0)
      47             :         {
      48             :                 return -1;
      49             :         }
      50             : 
      51          53 :         int result = info->ai_family;
      52          53 :         freeaddrinfo (info);
      53          53 :         return result;
      54             : }
      55             : 
      56          55 : static void addAddressHierarchy (Key * key, char * fieldbuffer)
      57             : {
      58             :         /* determine whether this is an ipv4 or ipv6 entry */
      59          55 :         int family = getAddressFamily (fieldbuffer);
      60             : 
      61             :         /* in case of an error default to ipv4 */
      62          55 :         switch (family)
      63             :         {
      64             :         case AF_INET6:
      65          16 :                 keyAddBaseName (key, "ipv6");
      66          16 :                 break;
      67             :         default:
      68          39 :                 keyAddBaseName (key, "ipv4");
      69             :         }
      70          55 : }
      71             : 
      72         142 : size_t elektraParseToken (char ** token, const char * line)
      73             : {
      74         142 :         size_t i = 0;
      75             : 
      76             :         /* skip whitespaces */
      77         381 :         while (line[i] == ' ' || line[i] == '\t')
      78          97 :                 i++;
      79             : 
      80             :         /* end of line or string, abort */
      81         142 :         if (line[i] == '\0' || line[i] == '\n') return 0;
      82             : 
      83             :         size_t start = i;
      84             : 
      85             :         /* count the number of characters in the token */
      86        1362 :         while (line[i] != ' ' && line[i] != '\t' && line[i] != '\0' && line[i] != '\n')
      87        1224 :                 i++;
      88             : 
      89         138 :         size_t tokenSize = i - start + 1;
      90         138 :         *token = (char *) elektraMalloc (tokenSize);
      91         138 :         strncpy (*token, line + start, tokenSize);
      92         138 :         (*token)[tokenSize - 1] = '\0';
      93             : 
      94         138 :         return i;
      95             : }
      96             : 
      97          53 : static void setOrderMeta (Key * key, int order)
      98             : {
      99             :         char buffer[MAX_ORDER_SIZE];
     100          53 :         snprintf (buffer, MAX_ORDER_SIZE, "%d", order);
     101          53 :         keySetMeta (key, "order", buffer);
     102          53 : }
     103             : 
     104         156 : static int parseComment (KeySet * comments, char * line, const char * commentStart, CommentConstructor constructor)
     105             : {
     106             :         /* count the number of whitespace characters before the comment */
     107         156 :         size_t spaces = elektraCountStartSpaces (line);
     108             : 
     109         156 :         if (*(line + spaces) == '\n')
     110             :         {
     111          59 :                 constructor (comments, spaces, 0, 0);
     112          59 :                 return 1;
     113             :         }
     114             : 
     115          97 :         size_t commentStartLen = strlen (commentStart);
     116          97 :         if (!strncmp (line + spaces, commentStart, commentStartLen))
     117             :         {
     118             :                 /* check for newlines */
     119          10 :                 char * newLine = strchr (line, '\n');
     120          10 :                 if (newLine)
     121             :                 {
     122          10 :                         *newLine = '\0';
     123             :                 }
     124             : 
     125          10 :                 constructor (comments, spaces, commentStart, line + spaces + commentStartLen);
     126          10 :                 return 1;
     127             :         }
     128             : 
     129             :         return 0;
     130             : }
     131             : 
     132          55 : static char * parseCanonicalName (Key * result, char * line)
     133             : {
     134             :         char * fieldBuffer;
     135          55 :         char * tokenPointer = line;
     136             : 
     137             :         /* read the ip address (if any) */
     138          55 :         int sret = elektraParseToken (&fieldBuffer, line);
     139             : 
     140          55 :         if (sret == 0) return 0;
     141             : 
     142          55 :         tokenPointer += sret;
     143             : 
     144             :         /* determine whether this is an ipv4 or ipv6 entry */
     145          55 :         addAddressHierarchy (result, fieldBuffer);
     146             : 
     147             :         /* store the ip address */
     148          55 :         keySetString (result, fieldBuffer);
     149             : 
     150          55 :         elektraFree (fieldBuffer);
     151             : 
     152             :         /* read the canonical name */
     153          55 :         sret = elektraParseToken (&fieldBuffer, tokenPointer);
     154             : 
     155          55 :         if (sret == 0) return 0;
     156             : 
     157          55 :         tokenPointer += sret;
     158          55 :         keyAddBaseName (result, fieldBuffer);
     159          55 :         elektraFree (fieldBuffer);
     160             : 
     161          55 :         return tokenPointer;
     162             : }
     163             : 
     164          32 : static char * parseAlias (KeySet * append, const Key * hostParent, char * tokenPointer)
     165             : {
     166             :         char * fieldBuffer;
     167          32 :         int sret = 0;
     168          32 :         sret = elektraParseToken (&fieldBuffer, tokenPointer);
     169          32 :         if (sret == 0) return 0;
     170             : 
     171          28 :         Key * alias = keyDup (hostParent);
     172          28 :         keyAddBaseName (alias, fieldBuffer);
     173          28 :         elektraFree (fieldBuffer);
     174             : 
     175             :         /* only add the alias if it does not exist already */
     176          28 :         if (ksLookup (append, alias, KDB_O_NONE))
     177             :         {
     178           2 :                 keyDel (alias);
     179             :         }
     180             :         else
     181             :         {
     182          26 :                 ksAppendKey (append, alias);
     183             :         }
     184             : 
     185          28 :         return tokenPointer + sret;
     186             : }
     187             : 
     188         102 : static int elektraKeySetMetaKeySet (Key * key, KeySet * metaKeySet)
     189             : {
     190         102 :         if (!key) return 0;
     191         102 :         if (!metaKeySet) return 0;
     192             : 
     193             :         Key * currentMeta;
     194         102 :         cursor_t initialCursor = ksGetCursor (metaKeySet);
     195         102 :         ksRewind (metaKeySet);
     196         301 :         while ((currentMeta = ksNext (metaKeySet)))
     197             :         {
     198          97 :                 keySetMeta (key, keyName (currentMeta), keyString (currentMeta));
     199             :         }
     200             : 
     201         102 :         ksSetCursor (metaKeySet, initialCursor);
     202             : 
     203         102 :         return 1;
     204             : }
     205             : 
     206         147 : int elektraHostsGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
     207             : {
     208         147 :         int errnosave = errno;
     209             :         char readBuffer[HOSTS_KDB_BUFFER_SIZE];
     210             : 
     211         147 :         if (!strcmp (keyName (parentKey), "system/elektra/modules/hosts"))
     212             :         {
     213          98 :                 KeySet * moduleConfig =
     214             : #include "contract.h"
     215          98 :                         ksAppend (returned, moduleConfig);
     216          98 :                 ksDel (moduleConfig);
     217          98 :                 return 1;
     218             :         }
     219             : 
     220          49 :         FILE * fp = fopen (keyValue (parentKey), "r");
     221             : 
     222          49 :         if (fp == 0)
     223             :         {
     224           0 :                 ELEKTRA_SET_ERROR_GET (parentKey);
     225           0 :                 errno = errnosave;
     226           0 :                 return -1;
     227             :         }
     228             : 
     229          49 :         ksClear (returned);
     230          49 :         KeySet * append = ksNew (ksGetSize (returned) * 2, KS_END);
     231             : 
     232          49 :         Key * key = keyDup (parentKey);
     233          49 :         ksAppendKey (append, key);
     234             : 
     235          49 :         Key * currentKey = 0;
     236          49 :         KeySet * comments = ksNew (0, KS_END);
     237          49 :         size_t order = 1;
     238          49 :         char * tokenPointer = 0;
     239             :         while (1)
     240             :         {
     241         124 :                 char * fret = fgets (readBuffer, HOSTS_KDB_BUFFER_SIZE, fp);
     242             : 
     243         124 :                 if (!fret) break;
     244             : 
     245          75 :                 if (!currentKey)
     246             :                 {
     247          57 :                         currentKey = keyDup (parentKey);
     248             :                 }
     249             : 
     250          75 :                 if (parseComment (comments, readBuffer, "#", &elektraAddLineComment)) continue;
     251             : 
     252          55 :                 tokenPointer = parseCanonicalName (currentKey, readBuffer);
     253          55 :                 if (tokenPointer == 0) continue;
     254             : 
     255             :                 /* canonical names have to be unique. If the hosts file contains
     256             :                  * duplicates, we honor only the first entry. This mirrors the
     257             :                  * behaviour of most name resolution implementations
     258             :                  */
     259          55 :                 if (ksLookup (append, currentKey, KDB_O_NONE))
     260             :                 {
     261           2 :                         keyDel (currentKey);
     262           2 :                         currentKey = 0;
     263           2 :                         ksClear (comments);
     264           2 :                         continue;
     265             :                 }
     266             : 
     267             :                 /* assign an order to the entry */
     268          53 :                 setOrderMeta (currentKey, order);
     269          53 :                 ++order;
     270             : 
     271          53 :                 ksAppendKey (append, currentKey);
     272             : 
     273             :                 /* Read in aliases */
     274             :                 while (1)
     275             :                 {
     276             :                         /* if we find a comment, there cannot be any more aliases */
     277          81 :                         if (parseComment (comments, tokenPointer, "#", &elektraAddInlineComment)) break;
     278             : 
     279             :                         /* if we reach the end of the line, there cannot be any more aliases */
     280          32 :                         tokenPointer = parseAlias (append, currentKey, tokenPointer);
     281          32 :                         if (tokenPointer == 0) break;
     282             :                 }
     283             : 
     284             :                 /* flush collected comments and start over with a new entry */
     285          53 :                 elektraKeySetMetaKeySet (currentKey, comments);
     286          53 :                 ksClear (comments);
     287          53 :                 currentKey = 0;
     288             :         }
     289             : 
     290          49 :         keyDel (currentKey);
     291             : 
     292          49 :         if (comments)
     293             :         {
     294             :                 /* save comments without a matching entry to the parentkey */
     295          49 :                 elektraKeySetMetaKeySet (parentKey, comments);
     296          49 :                 ksClear (comments);
     297          49 :                 ksDel (comments);
     298             :         }
     299             : 
     300             :         int ret;
     301          49 :         if (!ferror (fp))
     302             :         {
     303          49 :                 ksClear (returned);
     304          49 :                 ksAppend (returned, append);
     305          49 :                 ksDel (append);
     306          49 :                 ret = 1;
     307             :         }
     308             :         else
     309             :         {
     310           0 :                 ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "General parse error. Reason: %s", strerror (errno));
     311           0 :                 ksDel (append);
     312           0 :                 ret = -1;
     313             :         }
     314             : 
     315          49 :         fclose (fp);
     316          49 :         errno = errnosave;
     317          49 :         return ret;
     318             : }
     319             : 
     320         612 : Plugin * ELEKTRA_PLUGIN_EXPORT
     321             : {
     322             :         // clang-format off
     323         612 :         return elektraPluginExport("hosts",
     324             :                 ELEKTRA_PLUGIN_GET,     &elektraHostsGet,
     325             :                 ELEKTRA_PLUGIN_SET,     &elektraHostsSet,
     326             :                 ELEKTRA_PLUGIN_END);
     327             : }
     328             : 

Generated by: LCOV version 1.13