added gnutls backend and moved backend code into new files
[ircu2.10.12-pk.git] / ircd / s_conf.c
index 64e41c822bb69e87f8751e698d1b54d0ade76893..7db9ff2e4722e68136e3532d6cc7cc5446b475df 100644 (file)
@@ -34,7 +34,6 @@
 #include "hash.h"
 #include "ircd.h"
 #include "ircd_alloc.h"
-#include "ircd_auth.h"
 #include "ircd_chattr.h"
 #include "ircd_log.h"
 #include "ircd_reply.h"
@@ -49,6 +48,7 @@
 #include "opercmds.h"
 #include "parse.h"
 #include "res.h"
+#include "s_auth.h"
 #include "s_bsd.h"
 #include "s_debug.h"
 #include "s_misc.h"
@@ -94,7 +94,6 @@ static void killcomment(struct Client* sptr, const char* filename)
   FBFILE*     file = 0;
   char        line[80];
   struct stat sb;
-  struct tm*  tm;
 
   if (NULL == (file = fbopen(filename, "r"))) {
     send_reply(sptr, ERR_NOMOTD);
@@ -103,7 +102,6 @@ static void killcomment(struct Client* sptr, const char* filename)
     return;
   }
   fbstat(&sb, file);
-  tm = localtime((time_t*) &sb.st_mtime);        /* NetBSD needs cast */
   while (fbgets(line, sizeof(line) - 1, file)) {
     char* end = line + strlen(line);
     while (end > line) {
@@ -129,9 +127,7 @@ struct ConfItem* make_conf(int type)
 
   aconf = (struct ConfItem*) MyMalloc(sizeof(struct ConfItem));
   assert(0 != aconf);
-#ifdef        DEBUGMODE
   ++GlobalConfCount;
-#endif
   memset(aconf, 0, sizeof(struct ConfItem));
   aconf->status  = type;
   aconf->next    = GlobalConfList;
@@ -150,15 +146,16 @@ void free_conf(struct ConfItem *aconf)
          aconf->address.port));
   if (aconf->dns_pending)
     delete_resolver_queries(aconf);
+  MyFree(aconf->username);
   MyFree(aconf->host);
+  MyFree(aconf->origin_name);
   if (aconf->passwd)
     memset(aconf->passwd, 0, strlen(aconf->passwd));
   MyFree(aconf->passwd);
   MyFree(aconf->name);
+  MyFree(aconf->hub_limit);
   MyFree(aconf);
-#ifdef        DEBUGMODE
   --GlobalConfCount;
-#endif
 }
 
 /** Disassociate configuration from the client.
@@ -222,22 +219,19 @@ void conf_parse_userhost(struct ConfItem *aconf, char *host)
     aconf->addrbits = addrbits;
   else
     aconf->addrbits = -1;
-  MyFree(host);
 }
 
 /** Copies a completed DNS query into its ConfItem.
  * @param vptr Pointer to struct ConfItem for the block.
  * @param hp DNS reply, or NULL if the lookup failed.
  */
-static void conf_dns_callback(void* vptr, struct DNSReply* hp)
+static void conf_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name)
 {
   struct ConfItem* aconf = (struct ConfItem*) vptr;
   assert(aconf);
   aconf->dns_pending = 0;
-  if (hp) {
-    memcpy(&aconf->address.addr, &hp->addr, sizeof(aconf->address.addr));
-    MyFree(hp);
-  }
+  if (addr)
+    memcpy(&aconf->address.addr, addr, sizeof(aconf->address.addr));
 }
 
 /** Start a nameserver lookup of the conf host.  If the conf entry is
@@ -248,13 +242,9 @@ static void conf_dns_lookup(struct ConfItem* aconf)
 {
   if (!aconf->dns_pending) {
     char            buf[HOSTLEN + 1];
-    struct DNSQuery query;
-    query.vptr     = aconf;
-    query.callback = conf_dns_callback;
-    host_from_uh(buf, aconf->host, HOSTLEN);
-    buf[HOSTLEN] = '\0';
 
-    gethost_byname(buf, &query);
+    host_from_uh(buf, aconf->host, HOSTLEN);
+    gethost_byname(buf, conf_dns_callback, aconf);
     aconf->dns_pending = 1;
   }
 }
@@ -352,22 +342,6 @@ void det_confs_butmask(struct Client* cptr, int mask)
   }
 }
 
-/** Check client limits and attach Client block.
- * If there are more connections from the IP than \a aconf->maximum
- * allows, return ACR_TOO_MANY_FROM_IP.  Otherwise, attach \a aconf to
- * \a cptr.
- * @param cptr Client getting \a aconf.
- * @param aconf Configuration item to attach.
- * @return Authorization check result.
- */
-static enum AuthorizationCheckResult
-check_limit_and_attach(struct Client* cptr, struct ConfItem* aconf)
-{
-  if (IPcheck_nr(cptr) > aconf->maximum)
-    return ACR_TOO_MANY_FROM_IP;
-  return attach_conf(cptr, aconf);
-}
-
 /** Find the first (best) Client block to attach.
  * @param cptr Client for whom to check rules.
  * @return Authorization check result.
@@ -375,29 +349,169 @@ check_limit_and_attach(struct Client* cptr, struct ConfItem* aconf)
 enum AuthorizationCheckResult attach_iline(struct Client* cptr)
 {
   struct ConfItem* aconf;
-  struct DNSReply* hp;
 
   assert(0 != cptr);
 
-  hp = cli_dns_reply(cptr);
   for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
     if (aconf->status != CONF_CLIENT)
       continue;
+    /* If you change any of this logic, please make corresponding
+     * changes in conf_debug_iline() below.
+     */
     if (aconf->address.port && aconf->address.port != cli_listener(cptr)->addr.port)
       continue;
-    if (aconf->username) {
+    if (aconf->username && match(aconf->username, cli_username(cptr)))
+      continue;
+    if (aconf->host && match(aconf->host, cli_sockhost(cptr)))
+      continue;
+    if ((aconf->addrbits >= 0)
+        && !ipmask_check(&cli_ip(cptr), &aconf->address.addr, aconf->addrbits))
+      continue;
+    if (IPcheck_nr(cptr) > aconf->maximum)
+      return ACR_TOO_MANY_FROM_IP;
+    if (aconf->username)
       SetFlag(cptr, FLAG_DOID);
-      if (match(aconf->username, cli_username(cptr)))
+    return attach_conf(cptr, aconf);
+  }
+  return ACR_NO_AUTHORIZATION;
+}
+
+/** Interpret \a client as a client specifier and show which Client
+ * block(s) match that client.
+ *
+ * The client specifier may contain an IP address, hostname, listener
+ * port, or a combination of those separated by commas.  IP addresses
+ * and hostnamese may be preceded by "username@"; the last given
+ * username will be used for the match.
+ *
+ * @param[in] client Client specifier.
+ * @return Matching Client block structure.
+ */
+struct ConfItem *conf_debug_iline(const char *client)
+{
+  struct irc_in_addr address;
+  struct ConfItem *aconf;
+  struct DenyConf *deny;
+  char *sep;
+  unsigned short listener;
+  char username[USERLEN+1], hostname[HOSTLEN+1], realname[REALLEN+1];
+
+  /* Initialize variables. */
+  listener = 0;
+  memset(&address, 0, sizeof(address));
+  memset(&username, 0, sizeof(username));
+  memset(&hostname, 0, sizeof(hostname));
+  memset(&realname, 0, sizeof(realname));
+
+  /* Parse client specifier. */
+  while (*client) {
+    struct irc_in_addr tmpaddr;
+    long tmp;
+
+    /* Try to parse as listener port number first. */
+    tmp = strtol(client, &sep, 10);
+    if (tmp && (*sep == '\0' || *sep == ',')) {
+      listener = tmp;
+      client = sep + (*sep != '\0');
+      continue;
+    }
+
+    /* Maybe username@ before an IP address or hostname? */
+    tmp = strcspn(client, ",@");
+    if (client[tmp] == '@') {
+      if (tmp > USERLEN)
+        tmp = USERLEN;
+      ircd_strncpy(username, client, tmp);
+      /* and fall through */
+      client += tmp + 1;
+    }
+
+    /* Looks like an IP address? */
+    tmp = ircd_aton(&tmpaddr, client);
+    if (tmp && (client[tmp] == '\0' || client[tmp] == ',')) {
+        memcpy(&address, &tmpaddr, sizeof(address));
+        client += tmp + (client[tmp] != '\0');
         continue;
     }
-    if (aconf->host && (!hp || match(aconf->host, hp->h_name)))
+
+    /* Realname? */
+    if (client[0] == '$' && client[1] == 'R') {
+      client += 2;
+      for (tmp = 0; *client != '\0' && *client != ',' && tmp < REALLEN; ++client, ++tmp) {
+        if (*client == '\\')
+          realname[tmp] = *++client;
+        else
+          realname[tmp] = *client;
+      }
+      continue;
+    }
+
+    /* Else must be a hostname. */
+    tmp = strcspn(client, ",");
+    if (tmp > HOSTLEN)
+      tmp = HOSTLEN;
+    ircd_strncpy(hostname, client, tmp);
+    client += tmp + (client[tmp] != '\0');
+  }
+
+  /* Walk configuration to find matching Client block. */
+  for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
+    if (aconf->status != CONF_CLIENT)
+      continue;
+    if (aconf->address.port && aconf->address.port != listener) {
+      fprintf(stdout, "Listener port mismatch: %u != %u\n", aconf->address.port, listener);
       continue;
+    }
+    if (aconf->username && match(aconf->username, username)) {
+      fprintf(stdout, "Username mismatch: %s != %s\n", aconf->username, username);
+      continue;
+    }
+    if (aconf->host && match(aconf->host, hostname)) {
+      fprintf(stdout, "Hostname mismatch: %s != %s\n", aconf->host, hostname);
+      continue;
+    }
     if ((aconf->addrbits >= 0)
-        && !ipmask_check(&cli_ip(cptr), &aconf->address.addr, aconf->addrbits))
+        && !ipmask_check(&address, &aconf->address.addr, aconf->addrbits)) {
+      fprintf(stdout, "IP address mismatch: %s != %s\n", aconf->name, ircd_ntoa(&address));
       continue;
-    return check_limit_and_attach(cptr, aconf);
+    }
+    fprintf(stdout, "Match! username=%s host=%s ip=%s class=%s maxlinks=%u password=%s\n",
+            (aconf->username ? aconf->username : "(null)"),
+            (aconf->host ? aconf->host : "(null)"),
+            (aconf->name ? aconf->name : "(null)"),
+            ConfClass(aconf), aconf->maximum,
+            (aconf->passwd ? aconf->passwd : "(null)"));
+    break;
   }
-  return ACR_NO_AUTHORIZATION;
+
+  /* If no authorization, say so and exit. */
+  if (!aconf)
+  {
+    fprintf(stdout, "No authorization found.\n");
+    return NULL;
+  }
+
+  /* Look for a Kill block with the user's name on it. */
+  for (deny = denyConfList; deny; deny = deny->next) {
+    if (deny->usermask && match(deny->usermask, username))
+      continue;
+    if (deny->realmask && match(deny->realmask, realname))
+      continue;
+    if (deny->bits > 0) {
+      if (!ipmask_check(&address, &deny->address, deny->bits))
+        continue;
+    } else if (deny->hostmask && match(deny->hostmask, hostname))
+      continue;
+
+    /* Looks like a match; report it. */
+    fprintf(stdout, "Denied! usermask=%s realmask=\"%s\" hostmask=%s (bits=%u)\n",
+            deny->usermask ? deny->usermask : "(null)",
+            deny->realmask ? deny->realmask : "(null)",
+            deny->hostmask ? deny->hostmask : "(null)",
+            deny->bits);
+  }
+
+  return aconf;
 }
 
 /** Check whether a particular ConfItem is already attached to a
@@ -539,6 +653,7 @@ struct ConfItem* find_conf_exact(const char* name, struct Client *cptr, int stat
     else if (!ipmask_check(&cli_ip(cptr), &tmp->address.addr, tmp->addrbits))
       continue;
     if ((tmp->status & CONF_OPERATOR)
+        && (MaxLinks(tmp->conn_class) > 0)
         && (tmp->clients >= MaxLinks(tmp->conn_class)))
       continue;
     return tmp;
@@ -653,6 +768,7 @@ void conf_erase_deny_list(void)
     MyFree(p->hostmask);
     MyFree(p->usermask);
     MyFree(p->message);
+    MyFree(p->realmask);
     MyFree(p);
   }
   denyConfList = 0;
@@ -698,9 +814,9 @@ void clear_quarantines(void)
 static int conf_error;
 /** When non-zero, indicates that the configuration file was loaded at least once. */
 static int conf_already_read;
-extern FILE *yyin;
 extern void yyparse(void);
-extern void init_lexer(void);
+extern int init_lexer(void);
+extern void deinit_lexer(void);
 
 /** Read configuration file.
  * @return Zero on failure, non-zero on success. */
@@ -708,11 +824,11 @@ int read_configuration_file(void)
 {
   conf_error = 0;
   feature_unmark(); /* unmark all features for resetting later */
-  /* Now just open an fd. The buffering isn't really needed... */
-  init_lexer();
+  clear_nameservers(); /* clear previous list of DNS servers */
+  if (!init_lexer())
+    return 0;
   yyparse();
-  fclose(yyin);
-  yyin = NULL;
+  deinit_lexer();
   feature_mark(); /* reset unmarked features */
   conf_already_read = 1;
   return 1;
@@ -744,6 +860,36 @@ attach_conf_uworld(struct Client *cptr)
     attach_conf_uworld(lp->value.cptr);
 }
 
+/** Free all memory associated with service mapping \a smap.
+ * @param smap[in] The mapping to free.
+ */
+void free_mapping(struct s_map *smap)
+{
+  struct nick_host *nh, *next;
+  for (nh = smap->services; nh; nh = next)
+  {
+    next = nh->next;
+    MyFree(nh);
+  }
+  MyFree(smap->name);
+  MyFree(smap->command);
+  MyFree(smap->prepend);
+  MyFree(smap);
+}
+
+/** Unregister and free all current service mappings. */
+static void close_mappings(void)
+{
+  struct s_map *map, *next;
+
+  for (map = GlobalServiceMapList; map; map = next) {
+    next = map->next;
+    unregister_mapping(map);
+    free_mapping(map);
+  }
+  GlobalServiceMapList = NULL;
+}
+
 /** Reload the configuration file.
  * @param cptr Client that requested rehash (if a signal, &me).
  * @param sig Type of rehash (0 = oper-requested, 1 = signal, 2 =
@@ -796,18 +942,19 @@ int rehash(struct Client *cptr, int sig)
 
   clear_quarantines();
 
-  if (sig != 2)
-    restart_resolver();
-
   class_mark_delete();
   mark_listeners_closing();
-  iauth_mark_closing();
+  auth_mark_closing();
+  close_mappings();
 
   read_configuration_file();
 
+  if (sig != 2)
+    restart_resolver();
+
   log_reopen(); /* reopen log files */
 
-  iauth_close_unused();
+  auth_close_unused();
   close_listeners();
   class_delete_marked();         /* unless it fails */
 
@@ -913,28 +1060,16 @@ int find_kill(struct Client *cptr)
    *             -- Isomer
    */
   for (deny = denyConfList; deny; deny = deny->next) {
-    if (0 != match(deny->usermask, name))
+    if (deny->usermask && match(deny->usermask, name))
+      continue;
+    if (deny->realmask && match(deny->realmask, realname))
+      continue;
+    if (deny->bits > 0) {
+      if (!ipmask_check(&cli_ip(cptr), &deny->address, deny->bits))
+        continue;
+    } else if (deny->hostmask && match(deny->hostmask, host))
       continue;
 
-    if (EmptyString(deny->hostmask))
-      break;
-
-    if (deny->flags & DENY_FLAGS_REALNAME) { /* K: by real name */
-      if (0 == match(deny->hostmask + 2, realname))
-       break;
-    } else if (deny->flags & DENY_FLAGS_IP) { /* k: by IP */
-#ifdef DEBUGMODE
-      char tbuf1[SOCKIPLEN], tbuf2[SOCKIPLEN];
-      Debug((DEBUG_DEBUG, "ip: %s network: %s/%u",
-             ircd_ntoa_r(tbuf1, &cli_ip(cptr)), ircd_ntoa_r(tbuf2, &deny->address), deny->bits));
-#endif
-      if (ipmask_check(&cli_ip(cptr), &deny->address, deny->bits))
-        break;
-    }
-    else if (0 == match(deny->hostmask, host))
-      break;
-  }
-  if (deny) {
     if (EmptyString(deny->message))
       send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP,
                  ":Connection from your host is refused on this server.");
@@ -944,19 +1079,17 @@ int find_kill(struct Client *cptr)
       else
         send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", deny->message);
     }
+    return -1;
   }
-  else if ((agline = gline_lookup(cptr, 0))) {
+
+  if (!feature_bool(FEAT_DISABLE_GLINES) && (agline = gline_lookup(cptr, 0))) {
     /*
      * find active glines
      * added a check against the user's IP address to find_gline() -Kev
      */
     send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", GlineReason(agline));
-  }
-
-  if (deny)
-    return -1;
-  if (agline)
     return -2;
+  }
 
   return 0;
 }
@@ -1007,35 +1140,18 @@ int conf_check_server(struct Client *cptr)
   if (IsConnecting(cptr) || IsHandshake(cptr)) {
     c_conf = find_conf_byname(lp, cli_name(cptr), CONF_SERVER);
     if (!c_conf) {
-      sendto_opmask_butone(0, SNO_OLDSNO, "Connect Error: lost C:line for %s",
+      sendto_opmask_butone(0, SNO_OLDSNO,
+                           "Connect Error: lost Connect block for %s",
                            cli_name(cptr));
       det_confs_butmask(cptr, 0);
       return -1;
     }
   }
 
-  if (!c_conf) {
-    if (cli_dns_reply(cptr)) {
-      struct DNSReply* hp = cli_dns_reply(cptr);
-      const char*     name = hp->h_name;
-      /*
-       * If we are missing a C or N line from above, search for
-       * it under all known hostnames we have for this ip#.
-       */
-      if ((c_conf = find_conf_byhost(lp, hp->h_name, CONF_SERVER)))
-        ircd_strncpy(cli_sockhost(cptr), name, HOSTLEN);
-      else
-        c_conf = find_conf_byip(lp, &hp->addr, CONF_SERVER);
-    }
-    else {
-      /*
-       * Check for C lines with the hostname portion the ip number
-       * of the host the server runs on. This also checks the case where
-       * there is a server connecting from 'localhost'.
-       */
-      c_conf = find_conf_byhost(lp, cli_sockhost(cptr), CONF_SERVER);
-    }
-  }
+  /* Try finding the Connect block by DNS name and IP next. */
+  if (!c_conf && !(c_conf = find_conf_byhost(lp, cli_sockhost(cptr), CONF_SERVER)))
+        c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER);
+
   /*
    * Attach by IP# only if all other checks have failed.
    * It is quite possible to get here with the strange things that can