/* * File: domain.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include #include "../dlib/dlib.h" #include "msg.h" #include "list.h" #include "domain.h" typedef struct { char *origin; char *destination; } Rule; static Rule *exceptions = NULL; static int num_exceptions = 0; static int num_exceptions_max = 1; static bool_t default_deny = FALSE; /* * Parse domainrc. */ void a_Domain_parse(FILE *fp) { char *line; uint_t lineno = 0; _MSG("Reading domainrc...\n"); while ((line = dGetline(fp)) != NULL) { ++lineno; /* Remove leading and trailing whitespace */ dStrstrip(line); if (line[0] && line[0] != '#') { const char *delim = " \t"; char *tok1 = strtok(line, delim); char *tok2 = strtok(NULL, delim); if (strtok(NULL, delim) != NULL) { MSG("Domain: Ignoring extraneous text at end of line %u.\n", lineno); } if (!tok2) { MSG("Domain: Not enough fields in line %u.\n", lineno); } else { if (dStrAsciiCasecmp(tok1, "default") == 0) { if (dStrAsciiCasecmp(tok2, "deny") == 0) { default_deny = TRUE; MSG("Domain: Default deny.\n"); } else if (dStrAsciiCasecmp(tok2, "accept") == 0) { default_deny = FALSE; MSG("Domain: Default accept.\n"); } else { MSG("Domain: Default action \"%s\" not recognised.\n", tok2); } } else { a_List_add(exceptions, num_exceptions, num_exceptions_max); exceptions[num_exceptions].origin = dStrdup(tok1); exceptions[num_exceptions].destination = dStrdup(tok2); num_exceptions++; _MSG("Domain: Exception from %s to %s.\n", tok1, tok2); } } } dFree(line); } } void a_Domain_freeall(void) { int i = 0; for (i = 0; i < num_exceptions; i++) { dFree(exceptions[i].origin); dFree(exceptions[i].destination); } dFree(exceptions); } /* * Wildcard ('*') pattern always matches. * "example.org" pattern matches "example.org". * ".example.org" pattern matches "example.org" and "sub.example.org". */ static bool_t Domain_match(const char *host, const char *pattern) { int cmp = strcmp(pattern, "*"); if (cmp) { if (pattern[0] != '.') cmp = dStrAsciiCasecmp(host, pattern); else { int diff = strlen(host) - strlen(pattern); if (diff == -1) cmp = dStrAsciiCasecmp(host, pattern + 1); else if (diff >= 0) cmp = dStrAsciiCasecmp(host + diff, pattern); } } return cmp ? FALSE : TRUE; } /* * Is the resource at 'source' permitted to request the resource at 'dest'? */ bool_t a_Domain_permit(const DilloUrl *source, const DilloUrl *dest) { int i; bool_t ret; const char *source_host, *dest_host; if (default_deny == FALSE && num_exceptions == 0) return TRUE; source_host = URL_HOST(source); dest_host = URL_HOST(dest); if (dest_host[0] == '\0') { ret = source_host[0] == '\0' || !dStrAsciiCasecmp(URL_SCHEME(dest), "data"); if (ret == FALSE) MSG("Domain: DENIED %s -> %s.\n", source_host, URL_STR(dest)); return ret; } if (a_Url_same_organization(source, dest)) return TRUE; ret = default_deny ? FALSE : TRUE; for (i = 0; i < num_exceptions; i++) { if (Domain_match(source_host, exceptions[i].origin) && Domain_match(dest_host, exceptions[i].destination)) { ret = default_deny; _MSG("Domain: Matched rule from %s to %s.\n", exceptions[i].origin, exceptions[i].destination); break; } } if (ret == FALSE) { const char *src = source_host[0] ? source_host : URL_STR(source); MSG("Domain: DENIED %s -> %s.\n", src, dest_host); } return ret; }