#include "struct.h"
#include "sys.h"
-#include <assert.h>
-#include <arpa/inet.h>
+/* #include <assert.h> -- Now using assert in ircd_log.h */
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
/** Global list of all ConfItem structures. */
struct ConfItem *GlobalConfList;
-/** Count of items in #GlobalConfLis. */
+/** Count of items in #GlobalConfList. */
int GlobalConfCount;
/** Global list of service mappings. */
struct s_map *GlobalServiceMapList;
}
}
+/** Parse a user\@host mask into username and host or IP parts.
+ * If \a host contains no username part, set \a aconf->username to
+ * NULL. If the host part of \a host looks like an IP mask, set \a
+ * aconf->addrbits and \a aconf->address to match. Otherwise, set
+ * \a aconf->host, and set \a aconf->addrbits to -1.
+ * @param[in,out] aconf Configuration item to set.
+ * @param[in] host user\@host mask to parse.
+ */
+void conf_parse_userhost(struct ConfItem *aconf, char *host)
+{
+ char *host_part;
+ unsigned char addrbits;
+
+ MyFree(aconf->username);
+ MyFree(aconf->host);
+ host_part = strchr(host, '@');
+ if (host_part) {
+ *host_part = '\0';
+ DupString(aconf->username, host);
+ host_part++;
+ } else {
+ aconf->username = NULL;
+ host_part = host;
+ }
+ DupString(aconf->host, host_part);
+ if (ipmask_parse(aconf->host, &aconf->address.addr, &addrbits))
+ 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.
}
/** Check client limits and attach Client block.
- * If the password field consists of one or two digits, use that
- * as the per-IP connection limit; otherwise use 255.
+ * 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)
{
- int number = 255;
-
- if (aconf->passwd) {
- if (IsDigit(*aconf->passwd) && !aconf->passwd[1])
- number = *aconf->passwd-'0';
- else if (IsDigit(*aconf->passwd) && IsDigit(aconf->passwd[1]) &&
- !aconf->passwd[2])
- number = (*aconf->passwd-'0')*10+(aconf->passwd[1]-'0');
- }
- if (IPcheck_nr(cptr) > number)
+ if (IPcheck_nr(cptr) > aconf->maximum)
return ACR_TOO_MANY_FROM_IP;
return attach_conf(cptr, aconf);
}
* @param cptr Client for whom to check rules.
* @return Authorization check result.
*/
-enum AuthorizationCheckResult attach_iline(struct Client* cptr)
+enum AuthorizationCheckResult attach_iline(struct Client* cptr)
{
struct ConfItem* aconf;
- static char uhost[HOSTLEN + USERLEN + 3];
- static char fullname[HOSTLEN + 1];
struct DNSReply* hp;
assert(0 != 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->host || !aconf->name)
+ if (aconf->username) {
+ SetFlag(cptr, FLAG_DOID);
+ if (match(aconf->username, cli_username(cptr)))
+ continue;
+ }
+ if (aconf->host && (!hp || match(aconf->host, hp->h_name)))
+ continue;
+ if ((aconf->addrbits >= 0)
+ && !ipmask_check(&cli_ip(cptr), &aconf->address.addr, aconf->addrbits))
continue;
- if (hp) {
- ircd_strncpy(fullname, hp->h_name, HOSTLEN);
- fullname[HOSTLEN] = '\0';
+ return check_limit_and_attach(cptr, aconf);
+ }
+ return ACR_NO_AUTHORIZATION;
+}
- Debug((DEBUG_DNS, "a_il: %s->%s", cli_sockhost(cptr), fullname));
+/** 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;
+ char *sep;
+ unsigned short listener;
+ char username[USERLEN+1], hostname[HOSTLEN+1];
+
+ /* Initialize variables. */
+ listener = 0;
+ memset(&address, 0, sizeof(address));
+ memset(&username, 0, sizeof(username));
+ memset(&hostname, 0, sizeof(hostname));
+
+ /* 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;
+ }
- if (strchr(aconf->name, '@')) {
- strcpy(uhost, cli_username(cptr));
- strcat(uhost, "@");
- }
- else
- *uhost = '\0';
- strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost));
- uhost[sizeof(uhost) - 1] = 0;
- if (0 == match(aconf->name, uhost)) {
- if (strchr(uhost, '@'))
- SetFlag(cptr, FLAG_DOID);
- return check_limit_and_attach(cptr, aconf);
- }
+ /* 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;
}
- if (strchr(aconf->host, '@')) {
- ircd_strncpy(uhost, cli_username(cptr), sizeof(uhost) - 2);
- uhost[sizeof(uhost) - 2] = 0;
- strcat(uhost, "@");
+
+ /* 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;
}
- else
- *uhost = '\0';
- strncat(uhost, cli_sock_ip(cptr), sizeof(uhost) - 1 - strlen(uhost));
- uhost[sizeof(uhost) - 1] = 0;
- if (match(aconf->host, uhost))
- continue;
- if (strchr(uhost, '@'))
- SetFlag(cptr, FLAG_DOID);
- return check_limit_and_attach(cptr, aconf);
+ /* Else must be a hostname. */
+ tmp = strcspn(client, ",");
+ if (tmp > HOSTLEN)
+ tmp = HOSTLEN;
+ ircd_strncpy(hostname, client, tmp);
+ client += tmp + (client[tmp] != '\0');
}
- return ACR_NO_AUTHORIZATION;
+
+ /* 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(&address, &aconf->address.addr, aconf->addrbits)) {
+ fprintf(stdout, "IP address mismatch: %s != %s\n", aconf->name, ircd_ntoa(&address));
+ continue;
+ }
+ 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);
+ return aconf;
+ }
+
+ fprintf(stdout, "No matches found.\n");
+ return NULL;
}
/** Check whether a particular ConfItem is already attached to a
/** Associate a specific configuration entry to a *local* client (this
* is the one which used in accepting the connection). Note, that this
- * automaticly changes the attachment if there was an old one...
+ * automatically changes the attachment if there was an old one...
* @param cptr Client to attach \a aconf to
* @param aconf ConfItem to attach
* @return Authorization check result.
/** Find a ConfItem that has the same name and user+host fields as
* specified. Requires an exact match for \a name.
* @param name Name to match
- * @param user User part of match (or NULL)
- * @param host Hostname part of match
+ * @param cptr Client to match against
* @param statmask Filter for ConfItem::status
* @return First found matching ConfItem.
*/
-struct ConfItem* find_conf_exact(const char* name, const char* user,
- const char* host, int statmask)
+struct ConfItem* find_conf_exact(const char* name, struct Client *cptr, int statmask)
{
struct ConfItem *tmp;
- char userhost[USERLEN + HOSTLEN + 3];
-
- if (user)
- ircd_snprintf(0, userhost, sizeof(userhost), "%s@%s", user, host);
- else
- ircd_strncpy(userhost, host, sizeof(userhost) - 1);
for (tmp = GlobalConfList; tmp; tmp = tmp->next) {
if (!(tmp->status & statmask) || !tmp->name || !tmp->host ||
0 != ircd_strcmp(tmp->name, name))
continue;
- /*
- * Accept if the *real* hostname (usually sockecthost)
- * socket host) matches *either* host or name field
- * of the configuration.
- */
- if (match(tmp->host, userhost))
+ if (tmp->username
+ && (EmptyString(cli_username(cptr))
+ || match(tmp->username, cli_username(cptr))))
continue;
- if (tmp->status & CONF_OPERATOR) {
- if (tmp->clients < MaxLinks(tmp->conn_class))
- return tmp;
- else
+ if (tmp->addrbits < 0)
+ {
+ if (match(tmp->host, cli_sockhost(cptr)))
continue;
}
- else
- return tmp;
+ else if (!ipmask_check(&cli_ip(cptr), &tmp->address.addr, tmp->addrbits))
+ continue;
+ if ((tmp->status & CONF_OPERATOR)
+ && (tmp->clients >= MaxLinks(tmp->conn_class)))
+ continue;
+ return tmp;
}
return 0;
}
conf_error = 1;
}
+/** Attach CONF_UWORLD items to a server and everything attached to it. */
+static void
+attach_conf_uworld(struct Client *cptr)
+{
+ struct DLink *lp;
+
+ attach_confs_byhost(cptr, cli_name(cptr), CONF_UWORLD);
+ for (lp = cli_serv(cptr)->down; lp; lp = lp->next)
+ attach_conf_uworld(lp->value.cptr);
+}
+
/** 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 =
for (i = 0; i <= HighestFd; i++) {
if ((acptr = LocalClientArray[i])) {
assert(!IsMe(acptr));
- if (IsServer(acptr)) {
+ if (IsServer(acptr))
det_confs_butmask(acptr, ~(CONF_UWORLD | CONF_ILLEGAL));
- attach_confs_byname(acptr, cli_name(acptr), CONF_UWORLD);
- }
/* Because admin's are getting so uppity about people managing to
* get past K/G's etc, we'll "fix" the bug by actually explaining
* whats going on.
}
}
+ attach_conf_uworld(&me);
+
return ret;
}
* attach the Connect block to the client structure for later use.
*/
attach_conf(cptr, c_conf);
- attach_confs_byname(cptr, cli_name(cptr), CONF_UWORLD);
if (!irc_in_addr_valid(&c_conf->address.addr))
memcpy(&c_conf->address.addr, &cli_ip(cptr), sizeof(c_conf->address.addr));