# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
#
-# Example ircd.conf file for Serene5.28 or newer.
+# Example ircd.conf file for Serene IRCD.
#
#
-# In this file I will explain what the different lines in the ircd.conf file
-# do. You may want to test config changes with chkconf before you try them
-# on your server to prevent disruption to your users due to errors.
+# This file uses a block-based configuration format. Each configuration
+# directive is a named block with key-value pairs inside curly braces.
+# Lines are terminated with semicolons. Comments begin with #.
#
-# In this file I use the term "ircd" to designate an irc server (daemon).
-#
-# The following terms can be used to determine if you need a line or not:
-# MANDATORY: The ircd requires the line to operate.
-# NETWORKED: The ircd needs the line when it is linked to one or more other
-# servers
+# The following terms can be used to determine if you need a block or not:
+# MANDATORY: The ircd requires the block to operate.
+# NETWORKED: The ircd needs the block when it is linked to other servers.
# SUGGESTED: It would most likely be a good idea if you used this.
# OPTIONAL: It's up to the person doing the configuration.
# DISCOURAGED: Probably not a good idea, except in limited circumstances.
-# OBSOLETE: Old, out of date, don't use this
#
-# NOTE: Many networks will require certain configuration lines to allow
-# their networks to run more smoothly. If you are given lines by
-# the network you are linking to then they should be implemented
+# NOTE: Many networks will require certain configuration to allow
+# their networks to run more smoothly. If you are given config
+# by the network you are linking to then they should be implemented
# regardless of this file's recommendation.
#
-# This file goes through the lines in the order they should be placed in
-# a functional ircd.conf file.
-#
-# NOTE: Keep in mind that this file is read in REVERSE, that means the
-# bottom line is the first one read and the top line is the last.
-# Remember this when you are doing anything that needs to be in
-# a specific order such as I-lines.
-#
-#
-#
-# M: MANDATORY. This line specifies the basic information the server needs
-# about itself to operate.
-#
-# M:server name:IP address:Server description:port
-# Server name is the name the server shows to clients and other servers on
-# the network.
-# IP address tells the server what address on the machine to bind to, if
-# you fill the field with a "*", or leave it empty, it will bind itself to
-# all addresses it finds on the machine.
-# Server description is the description of the server shown to clients and
-# other servers on the network.
-# Port is the default port the server will attach itself to.
-#
-# This line names the server Server1.Serenity-IRC.Net and attaches itself to
-# all addresses attached to the machine on port 7000:
-#
-M:Server1.Serenity-IRC.Net:*:Cool new server:7000
-#
-#
-#
-# A: MANDATORY. This is your administration info that is shown when someone
-# on your server types "/admin" This is a good place to keep the information
-# users need to contact someone in charge.
-#
-# ":" Designates a new line, other than that it's pretty much free form.
-#
-# Here is a format often used:
-# A:server's provider:admin: admin's email : coadmin: coadmin's email
-#
-A:Lame1 colocations:admin John Random: email JRandom@lame1.com
-#
-#
-#
-# Y: SUGGESTED. Defines connection class types and behavior.
-# Class is the number representing the Y line, Ping frequency defines the
-# duration between pings. Auto-connect frequency defines how often the server
-# will try to connect to the server defined with the corresponding connection
-# class: max connection defines the maximum number of connections the server
-# will allow using this connection class.
-#
-# Sendq is the maximum space the server will use to buffer outgoing
-# messages. If the sendq is filled, the affected connection is dropped.
-# I reccomend setting this low for clients (prevents flooding) and high
-# for servers.
-#
-# NOTE: The max connections for all Y-lines should add up to the total
-# number of connections the the server can handle MINUS the number of
-# file desrcriptors that are needed for things like wingate checking and one
-# for the config file. ( 5-10 should do)
-#
-# For example: A server compiled for 256 max filedescriptors should have all
-# Y-lines add up to 250 (remove 6 for server internal use)
-#
-#
-# Y:class:ping frequency:auto-connect frequency:max connections:max sendq
-#
-# This is a typical client Y-line no auto-connect, max 20 connections.
-Y:1:60:0:20:100000
-#
-# This is an example of a Y-line that could be used on your primary hub
-#
-Y:40:120:60:1:3000000
-# This is an example of a Y-line that could be used for your secondary hubs
-Y:50:120:0:5:3000000
-#
-#
-#
-# I: MANDATORY. These lines define who can connect to a server, without
-# them no one is able to connect to the server.
-#
-# I:IP mask:password:domain mask::Y-Line (optional)
-#
-# IP Mask is the numeric ip mask (ex 192.168.*) if Arbitrary text is
-# entered such as NOMATCH the I-line will only match based on the domain
-# name and will disallow clients from addresses that lack reverse lookup
-# The password is optional, and is needed to connect using that I line.
-#
-# The domain name is the name returned from a reverse DNS lookup, the Y-line
-# of course is the Y-line that you want to correspond to the connection.
-# NOTE: I-lines are read starting from the bottom passing though each one
-# until a line is found that matches the client's connection if none
-# is found access to the server is denied.
-#
-# This is a I-line that will allow anyone at all to connect.
-I:*@*::*@*::1
-#
-#
-#
-# P: SUGGESTED. Allows you to add ports and addresses/domain sockets other
-# than the one specified in the M-line.
-#
-# P:address:*:*:port
-#
-# To have the server bind all addresses available on a given port leave the
-# leave the field blank.
-# NOTE: if a given port is already in use the server will simply fail to
-# load.
-#
-# This line would have the server look for connections to port 7000 on
-# the address 127.0.0.1
-#
-P:127.0.0.1:*:*:7000
-#
-#
-#
-# O: SUGGESTED. O-lines define who has Oper access on the server. How
-# necessary these line are depend on how well your site is configured and the
-# philosophy of the network you are linked to.
-#
-# O:address:password:nick:flags:Y-line
-#
-# If the server is compiled to use encrypted Oper passwords then you must
-# use mkpasswd to convert the password from plaintext.
-#
-# NOTE: Addresses using the numeric IP address will work even if it
-# resolves.
+# NOTE: The config file is read top-to-bottom. For directives where order
+# matters (such as allow blocks), the first matching block wins.
+#
+#
+#
+# me {} - MANDATORY
+# Specifies the basic information the server needs about itself.
+#
+# name - The server name shown to clients and other servers.
+# info - The server description shown to clients and other servers.
+# address - IP address to bind to. Use "*" or omit to bind all addresses.
+# port - Default port the server will listen on.
+#
+me {
+ name Server1.Serenity-IRC.Net;
+ info "Cool new server";
+ address *;
+ port 7000;
+};
+
+#
+# admin {} - MANDATORY
+# Administration info shown when someone types /admin.
+# Each "line" directive adds a line of output.
+#
+admin {
+ line "Lame1 colocations";
+ line "admin John Random";
+ line "email JRandom@lame1.com";
+};
+
+#
+# class {} - SUGGESTED
+# Defines connection class types and behavior.
+#
+# name - Class number (referenced by other blocks).
+# pingfreq - Duration between pings in seconds.
+# connfreq - How often to auto-connect (0 = never).
+# maxlinks - Maximum number of connections in this class.
+# sendq - Maximum send queue size in bytes.
+#
+# NOTE: The maxlinks for all classes should add up to the total number of
+# connections the server can handle minus a few for internal use.
+#
+# Typical client class: no auto-connect, max 20 connections.
+class {
+ name 1;
+ pingfreq 60;
+ connfreq 0;
+ maxlinks 20;
+ sendq 100000;
+};
+
+# Primary hub class.
+class {
+ name 40;
+ pingfreq 120;
+ connfreq 60;
+ maxlinks 1;
+ sendq 3000000;
+};
+
+# Secondary hub class.
+class {
+ name 50;
+ pingfreq 120;
+ connfreq 0;
+ maxlinks 5;
+ sendq 3000000;
+};
+
+#
+# allow {} - MANDATORY (replaces I-lines)
+# Defines who can connect to this server. Without allow blocks, no one
+# can connect. The first matching block is used.
+#
+# ip - Numeric IP mask (e.g. 192.168.* or 2001:db8::*).
+# Use "NOMATCH" to only match by hostname.
+# host - Hostname mask from reverse DNS lookup.
+# password - Optional password required to connect.
+# class - Connection class number.
+# port - Optional: only match connections on this port.
+#
+# This block allows anyone to connect.
+allow {
+ ip *@*;
+ host *@*;
+ class 1;
+};
+
+#
+# listen {} - SUGGESTED (replaces P-lines)
+# Adds additional listen ports beyond the one in the me {} block.
+#
+# address - IP address to bind (omit or "*" for all addresses).
+# port - Port number to listen on.
+#
+listen {
+ address 127.0.0.1;
+ port 7000;
+};
+
+# Listen on an IPv6 address:
+#listen {
+# address "::1";
+# port 7000;
+#};
+
+#
+# oper {} - SUGGESTED (replaces O-lines)
+# Defines who has Oper access on the server.
+#
+# host - Address mask the oper must connect from.
+# password - Oper password (use mkpasswd if compiled with encryption).
+# name - Oper nickname.
+# flags - Access flags (see below).
+# class - Connection class number.
#
# flags:
-# a = can set +a Designates a services oper
-# A = can set +A to set server admin
-# b = can set temp klines using /kline
-# B = can remove temp klines using /unkline
-# c = can use /squit and /connect on locally connected servers
-# C = can use /squit and /connect on any server
-# D = can shut down the server using /die
-# f = can see server flood kills by setting /umode +f
-# g = can send globop messages
-# h = can see helpop requests by setting /umode +h
-# k = can kill clients connected locally
-# K = can kill any clients on the network
-# l = can send locop messages
-# n = can send local server messages (/msg $servername message to users)
-# N = can send global server messages (/msg $*.net message to users)
-# o = designates a local oper includes flags: bBcfghklnruw
-# O = designates a global oper includes flags: CKNo
-# r = can /rehash the server
-# R = can /restart the server
-# u = can see local connects and disconnects using /umode +c
-# w = can send wallops (obsolete)
+# a = can set +a (services oper) A = can set +A (server admin)
+# b = can set temp klines B = can remove temp klines
+# c = local /squit and /connect C = remote /squit and /connect
+# D = can /die the server f = can see flood kills (+f)
+# g = can send globops h = can see helpop requests (+h)
+# k = can kill local clients K = can kill any client
+# l = can send locops n = local server messages
+# N = global server messages o = local oper (includes bBcfghklnruw)
+# O = global oper (includes CKNo) r = can /rehash
+# R = can /restart u = can see connects (+c)
+# w = can send wallops
#
# NOTE: Access to everything is: AaDOR
#
-O:192.168.2.*:notarealpassword:AnOper:OR:10
-#
-#
-#
-# X: SUGGESTED. Sets a password for the /die and /restart commands.
-#
-# X:Die password:Restart password
-#
-X:killme:restartme
-#
-#
-#
-# C: NETWORKED. Connect lines, this line defines who your server can
-# connect to. C-lines must be paired with an N line to work.
-#
-# C:remote address:password:remote server name:port to auto-connect:Y-line
-# The address is the machine's Internet address, the server name is
-# the server's name on irc network as defined by the machine's M-line.
-# They do not have to be the same.
-# NOTE: Using the numeric ip address for the machine's address is slightly
-# faster and more secure than using its domain name.
-#
-C:192.168.2.1:notarealpassword:HubServer.Serenity-IRC.Net:7827:45
-#
-#
-#
-# N: NETWORKED. This line defines what servers your server can connect
-# to yours. N-lines must be paired with an C line to work correctly.
-#
-# N:remote address:password:remote server name::Y-line
-# The address is the machine's address on the Internet, the server name is
-# the server's name on irc network as defined by the machine's M-line.
-# They do not have to be the same.
-#
-# NOTE: Using the numeric IP address for the machine's address is slightly
-# faster and more secure than using its domain name.
-#
-N:192.168.2.1:notarealpassword:HubServer.Serenity-IRC.Net::40
-#
-#
-#
-# U: NETWORKED. Determines what servers may make changes to things like
-# channel modes without the server sounding alarms or attempting to stop
-# them.
-#
-# U:Server-Name:*:*
-#
-# This line allows services to have the access needed to do its job.
-#
-U:Services.Serenity-IRC.Net:*:*
-#
-#
-#
-# H: NETWORKED. Hub lines. These define what servers a given hub is permitted
-# to connect to the network.
-# H:servers the hub is allowed connect::name
-#
-# NOTE: The name is the name defined in the hub's M-line it might not be the
-# same as its address.
-#
-# This would allow HubServer.Serenity-IRC.Net to hub any servers while being directly
-# connected to your server.
-H:*::HubServer.Serenity-IRC.Net
-#
-# This line would only allow HubServer2 to have Mexican servers as leaves.
-H:*.mx.*:HubServer2.Serenity-IRC.Net
-#
-#
-#
-# G: OPTIONAL. General configuration options. These control runtime behavior
-# that was previously set at compile time.
-#
-# G:option
-#
-# Currently supported options:
-# hub - Enable hub mode. A hub server accepts multiple server connections
-# and routes traffic between them. Without this option, the server
-# runs as a leaf and will only maintain a single server link.
-# This setting is re-evaluated on /rehash.
-#
-# Uncomment the following line if this server is a hub:
-#G:hub
-#
-#
-#
-# Q: OPTIONAL. Nick quarantine. This line prevents non opers from using
-# nicks covered by the Q-line.
-#
-# Q::reason:nick
-#
-Q::users may not use services nicks:*serv*
-#
-#
-#
-# q: OBSOLETE. Server quarantine. This line quarantines the specified
-# server, the line must be on ALL SERVERS connected to the network or net
-# spits will occur.
-#
-# q::reason:server
-#
-q::I have no idea why:unknown.Serenity-IRC.Net
-#
-#
-#
-# K: OPTIONAL. Kill Line. K-lines are a means of denying access to
-# certain clients.
-#
-# K-lines work best for addresses that are likely to be banned long term
-# and therefore a temporary kline or akill won't do.
-#
-# K:address:reason:ident
-#
-# Address is the address or mask of the client you wish to ban.
-# NOTE: If you ban an IP address and reverse DNS is successful the
-# domain name returned will be checked and NOT the IP address, any bans
-# on the IP address will be IGNORED. This applies to both Klines
-# and A-kills.
-#
-# The reason field is what is shown to the client on the receiving end
-# of the kline.
-#
-# The ident is checked against what is returned by the users, keep in mind
-# that this is easily changeable, and not useful if you're trying to keep a
-# particular user off the network, although it is a lot more logical for temp
-# K-lines or A-kills since they are more readily changed.
-# This will ban any user from lame.com and tell them it was for mass
-# advertising:
-K:*.lame.com:Mass advertising:*
-#
-# This will ban anyone using the ident Sphere and tell them to get
-# a new script.
-K:*:Get a new script:Sphere
-#
-#
-# Z: DISCOURAGED. Disconnects the client at the earliest stage of connection.
-# useful for preventing annoying server messages by persistent attempted
-# server connections, or getting rid of people ban evading using vhosts.
-#
-# NOTE: 1 The address MUST be a numeric IP or it won't work.
-# NOTE: 2 The last field must be a * or strange things may happen to the
-# server.
-#
-# Z:address:reason:*
-#
-Z:192.168.2.2:not a server:*
+oper {
+ host 192.168.2.*;
+ password notarealpassword;
+ name AnOper;
+ flags OR;
+ class 10;
+};
+
+#
+# drpass {} - SUGGESTED (replaces X-lines)
+# Sets passwords for the /die and /restart commands.
+#
+drpass {
+ die killme;
+ restart restartme;
+};
+
+#
+# connect {} - NETWORKED (replaces C/N-line pairs)
+# Defines a server this server can link with. Each connect block
+# combines the old C-line (outgoing) and N-line (incoming) into one.
+#
+# host - Remote server's IP address or hostname.
+# password - Link password (must match on both ends).
+# name - Remote server's IRC name (from their me {} block).
+# port - Port to auto-connect on (0 = no auto-connect).
+# class - Connection class number.
+# hub - Optional: mask of servers this link may introduce.
+# Use "*" to allow any. Omit for leaf-only links.
+#
+connect {
+ host 192.168.2.1;
+ password notarealpassword;
+ name HubServer.Serenity-IRC.Net;
+ port 7827;
+ class 40;
+ hub *;
+};
+
+# A connect block where the remote is only allowed to hub Mexican servers:
+#connect {
+# host 192.168.2.2;
+# password anotherpassword;
+# name HubServer2.Serenity-IRC.Net;
+# port 0;
+# class 50;
+# hub *.mx.*;
+#};
+
+#
+# uworld {} - NETWORKED (replaces U-lines)
+# Determines which servers may make privileged changes (e.g. services).
+#
+uworld {
+ name Services.Serenity-IRC.Net;
+};
+
+#
+# general {} - OPTIONAL (replaces G-lines)
+# General runtime configuration options.
+#
+# hub - Enable hub mode. A hub accepts multiple server connections
+# and routes traffic between them. Without this, the server
+# runs as a leaf with a single server link.
+# Re-evaluated on /rehash.
+#
+# Uncomment the following to enable hub mode:
+#general {
+# hub yes;
+#};
+
+#
+# quarantine {} - OPTIONAL (replaces Q-lines for nicks)
+# Prevents non-opers from using matching nicknames.
+#
+# nick - Nick mask to quarantine (can use wildcards).
+# reason - Reason shown to users.
+#
+quarantine {
+ nick *serv*;
+ reason "users may not use services nicks";
+};
+
+#
+# squar {} - server quarantine (replaces q-lines)
+# Quarantines a server. Must be on ALL servers or netsplits will occur.
+#
+# name - Server name to quarantine.
+# reason - Reason for quarantine.
+#
+#squar {
+# name unknown.Serenity-IRC.Net;
+# reason "I have no idea why";
+#};
+
+#
+# kline {} - OPTIONAL (replaces K-lines)
+# Denies access to matching clients. Multiple host entries can share
+# a single reason, avoiding repetition.
+#
+# host - Address or mask to ban. Can be repeated for multiple hosts.
+# ident - Ident/username mask (default: *).
+# reason - Reason shown to the banned client.
+#
+# NOTE: If reverse DNS succeeds, the hostname is checked, not the IP.
+# To ban by IP when DNS resolves, add both hostname and IP bans.
+#
+# Ban all users from lame.com:
+kline {
+ host *.lame.com;
+ ident *;
+ reason "Mass advertising";
+};
+
+# Ban a specific ident from anywhere:
+kline {
+ host *;
+ ident Sphere;
+ reason "Get a new script";
+};
+
+# Ban multiple hosts for the same reason (replaces many old K-lines):
+#kline {
+# host *.spam1.example.com;
+# host *.spam2.example.com;
+# host *.spam3.example.com;
+# ident *;
+# reason "Spam network";
+#};
+
+#
+# zline {} - DISCOURAGED (replaces Z-lines)
+# Disconnects clients at the earliest stage of connection by IP.
+# Useful for persistent connection attempts or ban evasion via vhosts.
+#
+# address - Numeric IP address (must be numeric, not hostname).
+# reason - Reason for the ban.
+#
+zline {
+ address 192.168.2.2;
+ reason "not a server";
+};
+
+# IPv6 zline example:
+#zline {
+# address "2001:db8::dead:beef";
+# reason "not welcome";
+#};
extern char *MyMalloc (), *MyRealloc () ;
extern char *debugmode, *configfile, *sbrk0;
-extern char *getfield (char *);
extern void get_sockhost (aClient *, char *);
extern char *rpl_str (int), *err_str (int);
extern char *strerror (int);
#undef free
#define MyMalloc(x) malloc(x)
-#define Reg
-static void new_class ();
-static char *getfield (), confchar ();
-static int openconf (), validate ();
-static aClass *get_class ();
-static aConfItem *initconf ();
-
-static int numclasses = 0, *classarr = (int *) NULL, debugflag = 0;
+static int debugflag = 0;
static char *configfile = CONFIGFILE;
-static char nullfield[] = "";
-static char maxsendq[12];
+static int errors = 0;
+static int has_me = 0, has_admin = 0;
+static int num_chk_classes = 0;
+static int chk_classes[256];
-int main (int argc, char *argv[])
+static void note_class (int cn)
{
- new_class (0);
+ int i;
+ for (i = 0; i < num_chk_classes; i++)
+ if (chk_classes[i] == cn)
+ return;
+ if (num_chk_classes < 256)
+ chk_classes[num_chk_classes++] = cn;
+}
- if (chdir (DPATH)) {
- perror ("chdir");
- exit (-1);
- }
- if (argc > 1 && !strncmp (argv[1], "-d", 2)) {
- debugflag = 1;
- if (argv[1][2])
- debugflag = atoi (argv[1] + 2);
- argc--, argv++;
+static int class_exists (int cn)
+{
+ int i;
+ for (i = 0; i < num_chk_classes; i++)
+ if (chk_classes[i] == cn)
+ return 1;
+ return 0;
+}
+
+/* Read entire file into a malloc'd buffer */
+static char *read_file (const char *path)
+{
+ struct stat st;
+ char *buf;
+ int fd, n, total = 0;
+
+ fd = open (path, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+ if (fstat (fd, &st) < 0) {
+ close (fd);
+ return NULL;
}
- else if (argc > 1 && !strncmp (argv[1], "-h", 2)) {
- printf ("chkconf [-d] [conf file]\n");
- printf ("-d = debug mode\n");
- printf ("conf file = path to ircd.conf\n\n");
- exit (0);
+ buf = (char *) malloc (st.st_size + 1);
+ while (total < st.st_size) {
+ n = read (fd, buf + total, st.st_size - total);
+ if (n <= 0)
+ break;
+ total += n;
}
- if (argc > 1)
- configfile = argv[1];
- return validate (initconf ());
+ buf[total] = '\0';
+ close (fd);
+ return buf;
}
-/*
- * openconf
- *
- * returns -1 on any error or else the fd opened from which to read the
- * configuration file from.
- */
-static int openconf ()
+static char *skip_ws (char *p)
{
- return open (configfile, O_RDONLY);
+ while (*p) {
+ if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
+ p++;
+ else if (*p == '#') {
+ while (*p && *p != '\n')
+ p++;
+ } else
+ break;
+ }
+ return p;
}
-static int oper_access[] = {
- ~(OFLAG_ADMIN | OFLAG_SADMIN | OFLAG_ZLINE), '*',
- OFLAG_LOCAL, 'o',
- OFLAG_GLOBAL, 'O',
- OFLAG_REHASH, 'r',
- OFLAG_DIE, 'D',
- OFLAG_RESTART, 'R',
- OFLAG_GLOBOP, 'g',
- OFLAG_LOCOP, 'l',
- OFLAG_LROUTE, 'c',
- OFLAG_GROUTE, 'C',
- OFLAG_LKILL, 'k',
- OFLAG_GKILL, 'K',
- OFLAG_KLINE, 'b',
- OFLAG_UNKLINE, 'B',
- OFLAG_LNOTICE, 'n',
- OFLAG_GNOTICE, 'N',
- OFLAG_ADMIN, 'A',
- OFLAG_SADMIN, 'a',
- OFLAG_UMODEC, 'u',
- OFLAG_UMODEF, 'f',
- OFLAG_ZLINE, 'z',
- 0, 0
-};
+static char *get_token (char *p, char *dst, int dstsize)
+{
+ int i = 0;
+
+ p = skip_ws (p);
+ if (*p == '"') {
+ p++;
+ while (*p && *p != '"' && i < dstsize - 1) {
+ if (*p == '\\' && *(p + 1)) {
+ p++;
+ switch (*p) {
+ case 'n': dst[i++] = '\n'; break;
+ case 't': dst[i++] = '\t'; break;
+ case '\\': dst[i++] = '\\'; break;
+ case '"': dst[i++] = '"'; break;
+ default: dst[i++] = *p; break;
+ }
+ p++;
+ } else
+ dst[i++] = *p++;
+ }
+ if (*p == '"')
+ p++;
+ } else {
+ while (*p && *p != ' ' && *p != '\t' && *p != '\r' && *p != '\n'
+ && *p != ';' && *p != '{' && *p != '}' && i < dstsize - 1)
+ dst[i++] = *p++;
+ }
+ dst[i] = '\0';
+ return p;
+}
-/*
- ** initconf()
- ** Read configuration file.
- **
- ** returns -1, if file cannot be opened
- ** 0, if file opened
- */
-static aConfItem * initconf (int opt)
+/* Count approximate line number at position p within buf */
+static int line_number (char *buf, char *p)
{
- int fd;
- char line[512], *tmp, c[80], *s;
- int ccount = 0, ncount = 0, dh, flags = 0;
- int lineno;
- aConfItem *aconf = NULL, *ctop = NULL;
+ int n = 1;
+ char *s;
+ for (s = buf; s < p; s++)
+ if (*s == '\n')
+ n++;
+ return n;
+}
- (void) fprintf (stderr, "\nOpening %s as ircd configuration file\n\n",
- configfile);
- if ((fd = openconf ()) == -1) {
- return NULL;
+static int validate_conf (const char *path)
+{
+ char *filebuf, *p, *block_start;
+ char blocktype[512], key[512], value[512];
+ int has_connect_port;
+
+ filebuf = read_file (path);
+ if (!filebuf) {
+ fprintf (stderr, "ERROR: Cannot open %s\n", path);
+ return -1;
}
- lineno = 0;
- (void) dgets (-1, NULL, 0); /* make sure buffer is at empty pos */
- while ((dh = dgets (fd, line, sizeof (line) - 1)) > 0) {
- /* The +2 here is a silly hack, but it does cause the correct line number
- * to be reported when it actually reaches the end of file case. This is
- * the only place lineno should be incremented, otherwise we're causing
- * an off by one error in the line number for each error reported. -Studded
- */
- printf ("%u:\tEnd of file\r", (lineno++ + 2));
- if (aconf) {
- if (aconf->host)
- (void) free (aconf->host);
- if (aconf->passwd)
- (void) free (aconf->passwd);
- if (aconf->name)
- (void) free (aconf->name);
- }
- else
- aconf = (aConfItem *) malloc (sizeof (*aconf));
- aconf->host = (char *) NULL;
- aconf->passwd = (char *) NULL;
- aconf->name = (char *) NULL;
- aconf->class = (aClass *) NULL;
- if ((tmp = (char *) strchr (line, '\n')))
- *tmp = 0;
- else
- while (dgets (fd, c, sizeof (c) - 1))
- if ((tmp = (char *) strchr (c, '\n'))) {
- *tmp = 0;
- break;
- }
- /*
- * Do quoting of characters and # detection.
- */
- for (tmp = line; *tmp; tmp++) {
- if (*tmp == '\\') {
- switch (*(tmp + 1)) {
- case 'n':
- *tmp = '\n';
- break;
- case 'r':
- *tmp = '\r';
- break;
- case 't':
- *tmp = '\t';
- break;
- case '0':
- *tmp = '\0';
- break;
- default:
- *tmp = *(tmp + 1);
- break;
- }
- if (!*(tmp + 1))
- break;
- else
- for (s = tmp; (*s = *++s););
- tmp++;
- }
- else if (*tmp == '#')
- *tmp = '\0';
- }
- if (!*line || *line == '#' || *line == '\n' ||
- *line == ' ' || *line == '\t')
- continue;
- if (line[1] != ':') {
- (void) fprintf (stderr, "%u:\tERROR: Bad config line (%s)\n",
- lineno, line);
- continue;
+ fprintf (stderr, "Checking %s\n\n", path);
+
+ /* Class 0 always exists */
+ note_class (0);
+
+ p = filebuf;
+ while (*(p = skip_ws (p))) {
+ block_start = p;
+ p = get_token (p, blocktype, sizeof (blocktype));
+ p = skip_ws (p);
+ if (*p != '{') {
+ fprintf (stderr, "Line %d: ERROR: Expected '{' after '%s'\n",
+ line_number (filebuf, block_start), blocktype);
+ errors++;
+ break;
}
+ p++;
+
if (debugflag)
- (void) printf ("\n%s\n", line);
- (void) fflush (stdout);
+ printf ("Block: %s (line %d)\n", blocktype, line_number (filebuf, block_start));
+
+ /* Track fields seen in this block */
+ int has_name = 0, has_host = 0, has_port = 0, has_password = 0;
+ int has_class = 0, has_info = 0, has_line = 0;
+ int admin_lines = 0;
+ int class_num = -1;
+ int name_num = -1;
+ has_connect_port = 0;
+
+ while (*(p = skip_ws (p)) && *p != '}') {
+ char *key_pos = p;
+ p = get_token (p, key, sizeof (key));
+ if (!*key)
+ break;
+ p = get_token (p, value, sizeof (value));
+ p = skip_ws (p);
+ if (*p == ';')
+ p++;
+ else {
+ fprintf (stderr, "Line %d: WARNING: Missing ';' after '%s %s'\n",
+ line_number (filebuf, key_pos), key, value);
+ }
- tmp = getfield (line);
- if (!tmp) {
- (void) fprintf (stderr, "\tERROR: no fields found\n");
- continue;
+ if (!mycmp (key, "name")) { has_name = 1; name_num = atoi (value); }
+ else if (!mycmp (key, "host")) has_host = 1;
+ else if (!mycmp (key, "address")) has_host = 1;
+ else if (!mycmp (key, "ip")) has_host = 1;
+ else if (!mycmp (key, "port")) { has_port = 1; has_connect_port = atoi (value); }
+ else if (!mycmp (key, "password") || !mycmp (key, "passwd")) has_password = 1;
+ else if (!mycmp (key, "class")) { has_class = 1; class_num = atoi (value); }
+ else if (!mycmp (key, "info")) has_info = 1;
+ else if (!mycmp (key, "line")) { has_line = 1; admin_lines++; }
+ else if (!mycmp (key, "flags")) ;
+ else if (!mycmp (key, "hub")) ;
+ else if (!mycmp (key, "ident")) ;
+ else if (!mycmp (key, "reason")) ;
+ else if (!mycmp (key, "nick")) has_name = 1;
+ else if (!mycmp (key, "die")) ;
+ else if (!mycmp (key, "restart")) ;
+ else if (!mycmp (key, "pingfreq")) ;
+ else if (!mycmp (key, "connfreq")) ;
+ else if (!mycmp (key, "maxlinks")) ;
+ else if (!mycmp (key, "sendq")) ;
+ else if (!mycmp (key, "time")) ;
+ else {
+ fprintf (stderr, "Line %d: WARNING: Unknown key '%s' in %s block\n",
+ line_number (filebuf, key_pos), key, blocktype);
+ }
}
- aconf->status = CONF_ILLEGAL;
- switch (*tmp) {
- case 'A': /* Name, e-mail address of administrator */
- aconf->status = CONF_ADMIN;
- break;
- case 'a': /* of this server. */
- aconf->status = CONF_SADMIN;
- break;
- case 'C': /* Server where I should try to connect */
- case 'c': /* in case of lp failures */
- ccount++;
- aconf->status = CONF_CONNECT_SERVER;
- break;
- case 'E': /* Blocked DCC file type */
- aconf->status = CONF_DCCBLOCK;
- break;
- case 'f': /* Temp Z-line time */
- case 'F':
- aconf->status = CONF_ZTIME;
- break;
- case 'H': /* Hub server line */
- case 'h':
- aconf->status = CONF_HUB;
- break;
- case 'I': /* Just plain normal irc client trying */
- case 'i': /* to connect me */
- aconf->status = CONF_CLIENT;
- break;
- case 'K': /* Kill user line on irc.conf */
- case 'k':
- aconf->status = CONF_KILL;
- break;
- /* Me. Host field is name used for this host */
- /* and port number is the number of the port */
- case 'M':
- case 'm':
- aconf->status = CONF_ME;
- break;
- case 'N': /* Server where I should NOT try to */
- case 'n': /* connect in case of lp failures */
- /* but which tries to connect ME */
- ++ncount;
- aconf->status = CONF_NOCONNECT_SERVER;
- break;
- case 'O':
- aconf->status = CONF_OPERATOR;
- break;
- /* Local Operator, (limited privs --SRB)
- * Not anymore, OperFlag access levels. -Cabal95 */
- case 'o':
- aconf->status = CONF_OPERATOR;
- break;
- case 'P': /* listen port line */
- case 'p':
- aconf->status = CONF_LISTEN_PORT;
- break;
- case 'Q': /* a server that you don't want in your */
- case 'q': /* network. USE WITH CAUTION! */
- aconf->status = CONF_QUARANTINED_SERVER;
- break;
- case 'S': /* Service. Same semantics as */
- case 's': /* CONF_OPERATOR */
- aconf->status = CONF_SERVICE;
- break;
- case 'U':
- case 'u':
- aconf->status = CONF_UWORLD;
- break;
- case 'X':
- case 'x':
- aconf->status = CONF_DRPASS;
- break;
- case 'Y':
- case 'y':
- aconf->status = CONF_CLASS;
- break;
- case 'Z':
- case 'z':
- aconf->status = CONF_ZAP;
- break;
- default:
- (void) fprintf (stderr,
- "%u:\tERROR: unknown conf line letter (%c)\n",
- lineno, *tmp);
- break;
- }
+ if (*p == '}')
+ p++;
+ p = skip_ws (p);
+ if (*p == ';')
+ p++;
- if (IsIllegal (aconf))
- continue;
+ /* Validate required fields per block type */
+ int bline = line_number (filebuf, block_start);
- for (;;) { /* Fake loop, that I can use break here --msa */
- if ((tmp = getfield (NULL)) == NULL)
- break;
- DupString (aconf->host, tmp);
- if ((tmp = getfield (NULL)) == NULL)
- break;
- DupString (aconf->passwd, tmp);
- if ((tmp = getfield (NULL)) == NULL)
- break;
- DupString (aconf->name, tmp);
- if ((tmp = getfield (NULL)) == NULL)
- break;
- if (aconf->status & CONF_OPERATOR) {
- int *i, flag;
- char *m = "*";
- /*
- * Now we use access flags to define
- * what an operator can do with their O.
- */
- for (m = (*tmp) ? tmp : m; *m; m++) {
- for (i = oper_access; (flag = *i); i += 2)
- if (*m == (char) (*(i + 1))) {
- aconf->port |= flag;
- break;
- }
- if (flag == 0)
- fprintf (stderr,
- "%u:\tWARNING: Unknown oper access level '%c'\n",
- lineno, *m);
- }
- if (!(aconf->port & OFLAG_ISGLOBAL))
- aconf->status = CONF_LOCOP;
+ if (!mycmp (blocktype, "me")) {
+ if (has_me) {
+ fprintf (stderr, "Line %d: ERROR: Duplicate me {} block\n", bline);
+ errors++;
+ }
+ has_me = 1;
+ if (!has_name) {
+ fprintf (stderr, "Line %d: ERROR: me {} requires 'name'\n", bline);
+ errors++;
+ }
+ if (!has_port) {
+ fprintf (stderr, "Line %d: WARNING: me {} has no 'port'\n", bline);
}
- else
- aconf->port = atoi (tmp);
- if ((tmp = getfield (NULL)) == NULL)
- break;
- if (!(aconf->status & CONF_CLASS))
- aconf->class = get_class (atoi (tmp));
- break;
}
- if (!aconf->class && (aconf->status & (CONF_CONNECT_SERVER |
- CONF_NOCONNECT_SERVER |
- CONF_OPS | CONF_CLIENT))) {
- (void) fprintf (stderr, "%u:\tWARNING: No class. Default 0\n",
- lineno);
- aconf->class = get_class (0);
+ else if (!mycmp (blocktype, "admin")) {
+ if (has_admin) {
+ fprintf (stderr, "Line %d: ERROR: Duplicate admin {} block\n", bline);
+ errors++;
+ }
+ has_admin = 1;
+ if (admin_lines == 0) {
+ fprintf (stderr, "Line %d: WARNING: admin {} has no 'line' entries\n", bline);
+ }
}
- /* Check for bad Z-lines */
- if (aconf->status == CONF_ZAP) {
- char *tempc = aconf->host;
- if (!tempc) {
- fprintf (stderr, "%u:\tERROR: Bad Z-line\n", lineno);
+ else if (!mycmp (blocktype, "class")) {
+ if (!has_name && class_num < 0) {
+ fprintf (stderr, "Line %d: ERROR: class {} requires 'name'\n", bline);
+ errors++;
+ } else {
+ int cn = name_num >= 0 ? name_num : class_num;
+ note_class (cn);
+ }
+ }
+ else if (!mycmp (blocktype, "allow")) {
+ if (!has_host) {
+ fprintf (stderr, "Line %d: ERROR: allow {} requires 'ip' or 'host'\n", bline);
+ errors++;
+ }
+ if (has_class && !class_exists (class_num)) {
+ fprintf (stderr, "Line %d: WARNING: allow {} references undefined class %d\n",
+ bline, class_num);
}
- for (; *tempc; tempc++)
- if ((*tempc >= '0') && (*tempc <= '9'))
- goto zap_safe;
- fprintf (stderr, "%u:\tERROR: Z-line mask too broad\n", lineno);
- zap_safe:;
}
- /* Check for bad F lines */
- if (aconf->status == CONF_ZTIME) {
- if (!((aconf->host) && isdigit (*aconf->host)))
- (void) fprintf (stderr,
- "%u:\tERROR: F-lines must contain a digit in the first slot\n",
- lineno);
+ else if (!mycmp (blocktype, "listen")) {
+ if (!has_port) {
+ fprintf (stderr, "Line %d: ERROR: listen {} requires 'port'\n", bline);
+ errors++;
+ }
}
- /*
- ** If conf line is a class definition, create a class entry
- ** for it and make the conf_line illegal and delete it.
- */
- if (aconf->status & CONF_CLASS) {
- if (!aconf->host) {
- (void) fprintf (stderr, "\tERROR: no class #\n");
- continue;
+ else if (!mycmp (blocktype, "oper")) {
+ if (!has_host) {
+ fprintf (stderr, "Line %d: ERROR: oper {} requires 'host'\n", bline);
+ errors++;
+ }
+ if (!has_password) {
+ fprintf (stderr, "Line %d: ERROR: oper {} requires 'password'\n", bline);
+ errors++;
}
- if (!tmp) {
- (void) fprintf (stderr,
- "%u:\tWARNING: missing sendq field\n",
- lineno);
- (void) fprintf (stderr, "\t\t default: %d\n", MAXSENDQLENGTH);
- (void) sprintf (maxsendq, "%d", MAXSENDQLENGTH);
+ if (!has_name) {
+ fprintf (stderr, "Line %d: ERROR: oper {} requires 'name'\n", bline);
+ errors++;
}
- else
- (void) sprintf (maxsendq, "%d", atoi (tmp));
- new_class (atoi (aconf->host));
- aconf->class = get_class (atoi (aconf->host));
- goto print_confline;
}
- if (aconf->status & CONF_LISTEN_PORT) {
- if (!aconf->host)
- (void) fprintf (stderr, "\tERROR: %s\n",
- "null host field in P-line");
- else if (strchr (aconf->host, '/'))
- (void) fprintf (stderr,
- "%u:\tWARNING: / present in P-line "
- "UNIXPORT configuration no longer supported\n",
- lineno);
- aconf->class = get_class (0);
- goto print_confline;
+ else if (!mycmp (blocktype, "connect")) {
+ if (!has_host) {
+ fprintf (stderr, "Line %d: ERROR: connect {} requires 'host'\n", bline);
+ errors++;
+ }
+ if (!has_password) {
+ fprintf (stderr, "Line %d: ERROR: connect {} requires 'password'\n", bline);
+ errors++;
+ }
+ if (!has_name) {
+ fprintf (stderr, "Line %d: ERROR: connect {} requires 'name'\n", bline);
+ errors++;
+ }
+ if (has_class && !class_exists (class_num)) {
+ fprintf (stderr, "Line %d: WARNING: connect {} references undefined class %d\n",
+ bline, class_num);
+ }
}
- if (aconf->status & CONF_SERVER_MASK &&
- (!aconf->host || strchr (aconf->host, '*') ||
- strchr (aconf->host, '?'))) {
- (void) fprintf (stderr, "\tERROR: bad host field\n");
- continue;
+ else if (!mycmp (blocktype, "drpass")) {
+ /* Optional, no required fields */
}
- if (aconf->status & CONF_SERVER_MASK && BadPtr (aconf->passwd)) {
- (void) fprintf (stderr, "\tERROR: empty/no password field\n");
- continue;
+ else if (!mycmp (blocktype, "uworld")) {
+ if (!has_name) {
+ fprintf (stderr, "Line %d: ERROR: uworld {} requires 'name'\n", bline);
+ errors++;
+ }
}
- if (aconf->status & CONF_SERVER_MASK && !aconf->name) {
- (void) fprintf (stderr, "\tERROR: bad name field\n");
- continue;
+ else if (!mycmp (blocktype, "general")) {
+ /* All optional */
}
- if (aconf->status & (CONF_SERVER_MASK | CONF_OPS))
- if (!strchr (aconf->host, '@')) {
- char *newhost;
- int len = 3; /* *@\0 = 3 */
-
- len += strlen (aconf->host);
- newhost = (char *) MyMalloc (len);
- (void) sprintf (newhost, "*@%s", aconf->host);
- (void) free (aconf->host);
- aconf->host = newhost;
+ else if (!mycmp (blocktype, "quarantine")) {
+ if (!has_name) {
+ fprintf (stderr, "Line %d: WARNING: quarantine {} has no 'nick'\n", bline);
}
-
- if (!aconf->class)
- aconf->class = get_class (0);
- (void) sprintf (maxsendq, "%d", aconf->class->class);
-
- if (!aconf->name)
- aconf->name = nullfield;
- if (!aconf->passwd)
- aconf->passwd = nullfield;
- if (!aconf->host)
- aconf->host = nullfield;
- if (aconf->status & (CONF_ME | CONF_ADMIN)) {
- if (flags & aconf->status)
- (void) fprintf (stderr,
- "ERROR: multiple %c-lines\n",
- toupper (confchar (aconf->status)));
- else
- flags |= aconf->status;
}
- print_confline:
- if (debugflag > 8)
- (void) printf ("(%d) (%s) (%s) (%s) (%d) (%s)\n",
- aconf->status, aconf->host, aconf->passwd,
- aconf->name, aconf->port, maxsendq);
- (void) fflush (stdout);
- if (aconf->status & (CONF_SERVER_MASK | CONF_HUB)) {
- aconf->next = ctop;
- ctop = aconf;
- aconf = NULL;
+ else if (!mycmp (blocktype, "squar")) {
+ if (!has_name) {
+ fprintf (stderr, "Line %d: WARNING: squar {} has no 'name'\n", bline);
+ }
+ }
+ else if (!mycmp (blocktype, "kline")) {
+ if (!has_host) {
+ fprintf (stderr, "Line %d: ERROR: kline {} requires 'host'\n", bline);
+ errors++;
+ }
+ }
+ else if (!mycmp (blocktype, "zline")) {
+ if (!has_host) {
+ fprintf (stderr, "Line %d: ERROR: zline {} requires 'address'\n", bline);
+ errors++;
+ }
+ }
+ else if (!mycmp (blocktype, "service")) {
+ /* Optional */
+ }
+ else if (!mycmp (blocktype, "dccblock")) {
+ /* Optional */
+ }
+ else {
+ fprintf (stderr, "Line %d: WARNING: Unknown block type '%s'\n",
+ bline, blocktype);
}
}
- printf ("\n");
- (void) close (fd);
- return ctop;
-}
-static aClass * get_class (int cn)
-{
- static aClass cls;
- int i = numclasses - 1;
-
- cls.class = -1;
- for (; i >= 0; i--)
- if (classarr[i] == cn) {
- cls.class = cn;
- break;
- }
- if (i == -1)
- (void) fprintf (stderr, "\tWARNING: class %d not found\n", cn);
- return &cls;
-}
+ if (!has_me) {
+ fprintf (stderr, "ERROR: No me {} block found (required)\n");
+ errors++;
+ }
+ if (!has_admin) {
+ fprintf (stderr, "WARNING: No admin {} block found\n");
+ }
-static void new_class (int cn)
-{
- numclasses++;
- if (classarr)
- classarr = (int *) realloc (classarr, sizeof (int) * numclasses);
+ fprintf (stderr, "\n");
+ if (errors)
+ fprintf (stderr, "%d error(s) found.\n", errors);
else
- classarr = (int *) malloc (sizeof (int));
- classarr[numclasses - 1] = cn;
-}
+ fprintf (stderr, "Config file looks good.\n");
-/*
- * field breakup for ircd.conf file.
- */
-static char * getfield (char *newline)
-{
- static char *line = NULL;
- char *end, *field;
-
- if (newline)
- line = newline;
- if (line == NULL)
- return (NULL);
-
- field = line;
- if ((end = (char *) strchr (line, ':')) == NULL) {
- line = NULL;
- if ((end = (char *) strchr (field, '\n')) == NULL)
- end = field + strlen (field);
- }
- else
- line = end + 1;
- *end = '\0';
- return (field);
+ free (filebuf);
+ return errors ? -1 : 0;
}
-static int validate (aConfItem *top)
+int main (int argc, char *argv[])
{
- Reg aConfItem *aconf, *bconf;
- u_int otype = 0, valid = 0;
-
- if (!top)
- return 0;
-
- for (aconf = top; aconf; aconf = aconf->next) {
- if (aconf->status & CONF_MATCH)
- continue;
-
- if (aconf->status & CONF_SERVER_MASK) {
- if (aconf->status & CONF_CONNECT_SERVER)
- otype = CONF_NOCONNECT_SERVER;
- else if (aconf->status & CONF_NOCONNECT_SERVER)
- otype = CONF_CONNECT_SERVER;
-
- for (bconf = top; bconf; bconf = bconf->next) {
- if (bconf == aconf || !(bconf->status & otype))
- continue;
- if (bconf->class == aconf->class &&
- !mycmp (bconf->name, aconf->name) &&
- !mycmp (bconf->host, aconf->host)) {
- aconf->status |= CONF_MATCH;
- bconf->status |= CONF_MATCH;
- break;
- }
- }
- }
- else
- for (bconf = top; bconf; bconf = bconf->next) {
- if ((bconf == aconf) || !(bconf->status & CONF_SERVER_MASK))
- continue;
- if (!mycmp (bconf->name, aconf->name)) {
- aconf->status |= CONF_MATCH;
- break;
- }
- }
+ if (chdir (DPATH)) {
+ perror ("chdir");
+ exit (-1);
}
-
- (void) fprintf (stderr, "\n");
- for (aconf = top; aconf; aconf = aconf->next)
- if (aconf->status & CONF_MATCH)
- valid++;
- else
- (void) fprintf (stderr, "Unmatched %c:%s:%s:%s\n",
- confchar (aconf->status), aconf->host,
- aconf->passwd, aconf->name);
- return valid ? 0 : -1;
-}
-
-static char confchar (u_int status)
-{
- static char letrs[] = "QICNoOMKARYSLPH";
- char *s = letrs;
-
- status &= ~(CONF_MATCH | CONF_ILLEGAL);
-
- for (; *s; s++, status >>= 1)
- if (status & 1)
- return *s;
- return '-';
+ if (argc > 1 && !strncmp (argv[1], "-d", 2)) {
+ debugflag = 1;
+ if (argv[1][2])
+ debugflag = atoi (argv[1] + 2);
+ argc--, argv++;
+ }
+ else if (argc > 1 && !strncmp (argv[1], "-h", 2)) {
+ printf ("chkconf [-d] [conf file]\n");
+ printf ("-d = debug mode\n");
+ printf ("conf file = path to ircd.conf\n\n");
+ exit (0);
+ }
+ if (argc > 1)
+ configfile = argv[1];
+ return validate_conf (configfile);
}
int outofmemory ()
#endif
}
-/*
- * field breakup for ircd.conf file.
- */
-char *getfield (char *newline)
-{
- static char *line = NULL;
- char *end, *field;
-
- if (newline)
- line = newline;
- if (line == NULL)
- return (NULL);
-
- field = line;
- if ((end = (char *) strchr (line, ':')) == NULL) {
- line = NULL;
- if ((end = (char *) strchr (field, '\n')) == NULL)
- end = field + strlen (field);
- }
- else
- line = end + 1;
- *end = '\0';
- return (field);
-}
-
static int cancel_clients (aClient *cptr, aClient *sptr, char *cmd)
{
/*
{
return open (configfile, O_RDONLY);
}
-extern char *getfield ();
-
static int oper_access[] = {
~(OFLAG_ADMIN | OFLAG_SADMIN | OFLAG_ZLINE), '*',
OFLAG_LOCAL, 'o',
};
/*
- ** initconf()
- ** Read configuration file.
- **
- ** returns -1, if file cannot be opened
- ** 0, if file opened
+ * Block config parser.
+ *
+ * The config file uses a block-based format:
+ * blocktype {
+ * key value;
+ * key "quoted value";
+ * };
+ *
+ * Comments start with # and run to end of line.
+ */
+
+/* Read entire config file into a malloc'd buffer */
+static char *conf_read_file (int fd)
+{
+ struct stat st;
+ char *buf;
+ int n, total = 0;
+
+ if (fstat (fd, &st) < 0)
+ return NULL;
+ if (st.st_size > 1048576)
+ return NULL;
+ buf = (char *) MyMalloc (st.st_size + 1);
+ while (total < st.st_size) {
+ n = read (fd, buf + total, st.st_size - total);
+ if (n <= 0)
+ break;
+ total += n;
+ }
+ buf[total] = '\0';
+ return buf;
+}
+
+static char *conf_skip_ws (char *p)
+{
+ while (*p) {
+ if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
+ p++;
+ else if (*p == '#') {
+ while (*p && *p != '\n')
+ p++;
+ } else
+ break;
+ }
+ return p;
+}
+
+/* Parse a token (word or quoted string). Writes into dst. */
+static char *conf_get_token (char *p, char *dst, int dstsize)
+{
+ int i = 0;
+
+ p = conf_skip_ws (p);
+ if (*p == '"') {
+ p++;
+ while (*p && *p != '"' && i < dstsize - 1) {
+ if (*p == '\\' && *(p + 1)) {
+ p++;
+ switch (*p) {
+ case 'n': dst[i++] = '\n'; break;
+ case 't': dst[i++] = '\t'; break;
+ case '\\': dst[i++] = '\\'; break;
+ case '"': dst[i++] = '"'; break;
+ default: dst[i++] = *p; break;
+ }
+ p++;
+ } else
+ dst[i++] = *p++;
+ }
+ if (*p == '"')
+ p++;
+ } else {
+ while (*p && *p != ' ' && *p != '\t' && *p != '\r' && *p != '\n'
+ && *p != ';' && *p != '{' && *p != '}' && i < dstsize - 1)
+ dst[i++] = *p++;
+ }
+ dst[i] = '\0';
+ return p;
+}
+
+static int conf_parse_oper_flags (char *flags)
+{
+ int result = 0;
+ int *i, flag;
+ char *m;
+
+ for (m = flags; *m; m++)
+ for (i = oper_access; (flag = *i); i += 2)
+ if (*m == (char) (*(i + 1))) {
+ result |= flag;
+ break;
+ }
+ return result;
+}
+
+/* Add an aConfItem to the global conf list with post-processing */
+static void conf_add_item (aConfItem *aconf, int opt)
+{
+ if (aconf->status == CONF_ZAP) {
+ char *tempc = aconf->host;
+ if (!tempc) {
+ free_conf (aconf);
+ return;
+ }
+ for (; *tempc; tempc++)
+ if (*tempc >= '0' && *tempc <= '9')
+ goto zap_safe;
+ free_conf (aconf);
+ return;
+ zap_safe:;
+ }
+ if (aconf->status & (CONF_CLIENT_MASK | CONF_LISTEN_PORT)) {
+ if (Class (aconf) == 0)
+ Class (aconf) = find_class (0);
+ if (MaxLinks (Class (aconf)) < 0)
+ Class (aconf) = find_class (0);
+ }
+ if (aconf->status & (CONF_LISTEN_PORT | CONF_CLIENT)) {
+ aConfItem *bconf;
+
+ if ((bconf = find_conf_entry (aconf, aconf->status))) {
+ delist_conf (bconf);
+ bconf->status &= ~CONF_ILLEGAL;
+ if (aconf->status == CONF_CLIENT) {
+ bconf->class->links -= bconf->clients;
+ bconf->class = aconf->class;
+ if (bconf->class)
+ bconf->class->links += bconf->clients;
+ }
+ free_conf (aconf);
+ aconf = bconf;
+ }
+ else if (aconf->host && aconf->status == CONF_LISTEN_PORT)
+ (void) add_listener (aconf);
+ }
+ if (aconf->status & CONF_SERVER_MASK)
+ if (!aconf->host || strchr (aconf->host, '*') ||
+ strchr (aconf->host, '?') || !aconf->name) {
+ free_conf (aconf);
+ return;
+ }
+ if (aconf->status & (CONF_SERVER_MASK | CONF_LOCOP | CONF_OPERATOR))
+ if (aconf->host && !strchr (aconf->host, '@') && *aconf->host != '/') {
+ char *newhost;
+ int len = 3;
+
+ len += strlen (aconf->host);
+ newhost = (char *) MyMalloc (len);
+ (void) sprintf (newhost, "*@%s", aconf->host);
+ MyFree (aconf->host);
+ aconf->host = newhost;
+ }
+ if (aconf->status & CONF_SERVER_MASK) {
+ if (BadPtr (aconf->passwd)) {
+ free_conf (aconf);
+ return;
+ }
+ if (!(opt & BOOT_QUICK))
+ (void) lookup_confhost (aconf);
+ }
+ if (aconf->status == CONF_ME) {
+ strncpyzt (me.info, aconf->name, sizeof (me.info));
+ if (me.name[0] == '\0' && aconf->host[0])
+ strncpyzt (me.name, aconf->host, sizeof (me.name));
+ if (aconf->passwd[0] && (aconf->passwd[0] != '*'))
+ me.ip.s_addr = inet_addr (aconf->passwd);
+ else
+ me.ip.s_addr = INADDR_ANY;
+ if (portnum < 0 && aconf->port >= 0)
+ portnum = aconf->port;
+ }
+ if (aconf->status == CONF_KILL)
+ aconf->tmpconf = KLINE_PERM;
+ (void) collapse (aconf->host);
+ (void) collapse (aconf->name);
+ Debug ((DEBUG_NOTICE,
+ "Read Init: (%d) (%s) (%s) (%s) (%d) (%d)",
+ aconf->status, aconf->host, aconf->passwd,
+ aconf->name, aconf->port, Class (aconf)));
+ aconf->next = conf;
+ conf = aconf;
+}
+
+/*
+ * initconf()
+ * Read block-based configuration file.
+ *
+ * returns -1, if file cannot be opened
+ * 0, if file opened
*/
#define MAXCONFLINKS 150
+#define CONF_TOKLEN 512
+#define CONF_MAX_HOSTS 256
int initconf (int opt)
{
- static char quotes[9][2] = {
- {'b', '\b'},
- {'f', '\f'},
- {'n', '\n'},
- {'r', '\r'},
- {'t', '\t'},
- {'v', '\v'},
- {'\\', '\\'},
- {0, 0}
- };
- char *tmp, *s;
- int fd, i;
- char line[512], c[80];
- int ccount = 0, ncount = 0;
- aConfItem *aconf = NULL;
+ int fd;
+ char *filebuf, *p;
+ char blocktype[CONF_TOKLEN], key[CONF_TOKLEN], value[CONF_TOKLEN];
is_hub = 0;
Debug ((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile));
- if ((fd = openconf ()) == -1) {
+ if ((fd = openconf ()) == -1)
return -1;
- }
- (void) dgets (-1, NULL, 0); /* make sure buffer is at empty pos */
- while ((i = dgets (fd, line, sizeof (line) - 1)) > 0) {
- line[i] = '\0';
- if (line[i-2] == '\r') { /* Strip DOS type end of lines */
- line[i-2] = '\n';
- line[i-1] = '\0';
+
+ filebuf = conf_read_file (fd);
+ (void) close (fd);
+ if (!filebuf)
+ return -1;
+
+ p = filebuf;
+ while (*(p = conf_skip_ws (p))) {
+ p = conf_get_token (p, blocktype, sizeof (blocktype));
+ p = conf_skip_ws (p);
+ if (*p != '{') {
+ Debug ((DEBUG_ERROR, "Expected '{' after block type '%s'", blocktype));
+ break;
}
- if ((tmp = (char *) strchr (line, '\n')))
- *tmp = 0;
- else
- while (dgets (fd, c, sizeof (c) - 1) > 0)
- if ((tmp = (char *) strchr (c, '\n'))) {
- *tmp = 0;
- break;
- }
- /*
- * Do quoting of characters and # detection.
- */
- for (tmp = line; *tmp; tmp++) {
- if (*tmp == '\\') {
- for (i = 0; quotes[i][0]; i++)
- if (quotes[i][0] == *(tmp + 1)) {
- *tmp = quotes[i][1];
- break;
- }
- if (!quotes[i][0])
- *tmp = *(tmp + 1);
- if (!*(tmp + 1))
- break;
+ p++;
+
+ /* Per-block field storage */
+ char b_name[CONF_TOKLEN] = "";
+ char b_host[CONF_TOKLEN] = "";
+ char b_passwd[CONF_TOKLEN] = "";
+ char b_info[CONF_TOKLEN] = "";
+ char b_flags[CONF_TOKLEN] = "";
+ char b_hub[CONF_TOKLEN] = "";
+ char b_die[CONF_TOKLEN] = "";
+ char b_restart[CONF_TOKLEN] = "";
+ char b_admin[3][CONF_TOKLEN];
+ char b_hosts[CONF_MAX_HOSTS][CONF_TOKLEN];
+ int b_port = 0, b_class = 0;
+ int b_admin_count = 0, b_host_count = 0;
+ int b_hub_mode = 0;
+ int b_pingfreq = 0, b_connfreq = 0, b_maxlinks = 0, b_sendq = 0;
+ int b_zline_time = 0;
+
+ memset (b_admin, 0, sizeof (b_admin));
+
+ /* Parse key-value pairs */
+ while (*(p = conf_skip_ws (p)) && *p != '}') {
+ p = conf_get_token (p, key, sizeof (key));
+ if (!*key)
+ break;
+ p = conf_get_token (p, value, sizeof (value));
+ p = conf_skip_ws (p);
+ if (*p == ';')
+ p++;
+
+ if (!mycmp (key, "name"))
+ strncpyzt (b_name, value, sizeof (b_name));
+ else if (!mycmp (key, "host")) {
+ /* In allow{} blocks, "host" is the hostname mask (-> b_name),
+ * while "ip" is the IP mask (-> b_host). In kline{} blocks,
+ * "host" can be repeated for multi-host bans. */
+ if (!mycmp (blocktype, "allow"))
+ strncpyzt (b_name, value, sizeof (b_name));
else
- for (s = tmp; (*s = *(s + 1)); s++);
+ strncpyzt (b_host, value, sizeof (b_host));
+ if (b_host_count < CONF_MAX_HOSTS)
+ strncpyzt (b_hosts[b_host_count++], value, CONF_TOKLEN);
}
- else if (*tmp == '#')
- *tmp = '\0';
- }
- if (!*line || line[0] == '#' || line[0] == '\n' ||
- line[0] == ' ' || line[0] == '\t')
- continue;
- /* Could we test if it's conf line at all? -Vesa */
- if (line[1] != ':') {
- Debug ((DEBUG_ERROR, "Bad config line: %s", line));
- continue;
+ else if (!mycmp (key, "password") || !mycmp (key, "passwd"))
+ strncpyzt (b_passwd, value, sizeof (b_passwd));
+ else if (!mycmp (key, "address"))
+ strncpyzt (b_host, value, sizeof (b_host));
+ else if (!mycmp (key, "port"))
+ b_port = atoi (value);
+ else if (!mycmp (key, "class"))
+ b_class = atoi (value);
+ else if (!mycmp (key, "flags"))
+ strncpyzt (b_flags, value, sizeof (b_flags));
+ else if (!mycmp (key, "info"))
+ strncpyzt (b_info, value, sizeof (b_info));
+ else if (!mycmp (key, "line")) {
+ if (b_admin_count < 3)
+ strncpyzt (b_admin[b_admin_count++], value, CONF_TOKLEN);
+ }
+ else if (!mycmp (key, "ip"))
+ strncpyzt (b_host, value, sizeof (b_host));
+ else if (!mycmp (key, "hub")) {
+ if (!mycmp (blocktype, "general"))
+ b_hub_mode = (!mycmp (value, "yes") || !mycmp (value, "1"));
+ else
+ strncpyzt (b_hub, value, sizeof (b_hub));
+ }
+ else if (!mycmp (key, "ident"))
+ strncpyzt (b_name, value, sizeof (b_name));
+ else if (!mycmp (key, "reason"))
+ strncpyzt (b_passwd, value, sizeof (b_passwd));
+ else if (!mycmp (key, "nick"))
+ strncpyzt (b_name, value, sizeof (b_name));
+ else if (!mycmp (key, "die"))
+ strncpyzt (b_die, value, sizeof (b_die));
+ else if (!mycmp (key, "restart"))
+ strncpyzt (b_restart, value, sizeof (b_restart));
+ else if (!mycmp (key, "pingfreq"))
+ b_pingfreq = atoi (value);
+ else if (!mycmp (key, "connfreq"))
+ b_connfreq = atoi (value);
+ else if (!mycmp (key, "maxlinks"))
+ b_maxlinks = atoi (value);
+ else if (!mycmp (key, "sendq"))
+ b_sendq = atoi (value);
+ else if (!mycmp (key, "time"))
+ b_zline_time = atoi (value);
+ else
+ Debug ((DEBUG_ERROR, "Unknown key '%s' in block '%s'",
+ key, blocktype));
}
- if (aconf)
- free_conf (aconf);
- aconf = make_conf ();
- tmp = getfield (line);
- if (!tmp)
- continue;
- switch (*tmp) {
- case 'A': /* Name, e-mail address of administrator */
+ if (*p == '}')
+ p++;
+ p = conf_skip_ws (p);
+ if (*p == ';')
+ p++;
+
+ /* Create aConfItem(s) from block */
+
+ if (!mycmp (blocktype, "me")) {
+ /* M:servername:bindaddr:description:port */
+ aConfItem *aconf = make_conf ();
+ aconf->status = CONF_ME;
+ DupString (aconf->host, b_name);
+ DupString (aconf->passwd, b_host[0] ? b_host : "*");
+ DupString (aconf->name, b_info[0] ? b_info : b_name);
+ aconf->port = b_port;
+ conf_add_item (aconf, opt);
+ }
+ else if (!mycmp (blocktype, "admin")) {
+ /* A:line1:line2:line3 */
+ aConfItem *aconf = make_conf ();
aconf->status = CONF_ADMIN;
- break;
- case 'a': /* of this server. */
- aconf->status = CONF_SADMIN;
- break;
- case 'C': /* Server where I should try to connect */
- case 'c': /* in case of lp failures */
- ccount++;
- aconf->status = CONF_CONNECT_SERVER;
- break;
- case 'e':
- case 'E': /* Blocking of DCC transfers -GZ */
- aconf->status = CONF_DCCBLOCK;
- break;
- case 'f':
- case 'F':
- aconf->status = CONF_ZTIME;
- break;
- case 'G':
- case 'g':
- /* General config options */
- aconf->status = CONF_CONFIG;
- break;
- case 'H': /* Hub server line */
- case 'h':
- aconf->status = CONF_HUB;
- break;
- case 'I': /* Just plain normal irc client trying */
- case 'i': /* to connect me */
+ DupString (aconf->host, b_admin_count > 0 ? b_admin[0] : "");
+ DupString (aconf->passwd, b_admin_count > 1 ? b_admin[1] : "");
+ DupString (aconf->name, b_admin_count > 2 ? b_admin[2] : "");
+ conf_add_item (aconf, opt);
+ }
+ else if (!mycmp (blocktype, "class")) {
+ /* Y:class:pingfreq:connfreq:maxlinks:sendq */
+ int classnum = b_class ? b_class : atoi (b_name);
+ add_class (classnum, b_pingfreq, b_connfreq, b_maxlinks, b_sendq);
+ }
+ else if (!mycmp (blocktype, "allow")) {
+ /* I:ipmask:password:hostmask:port:class
+ * ip= -> aconf->host, host= -> aconf->name */
+ aConfItem *aconf = make_conf ();
aconf->status = CONF_CLIENT;
- break;
- case 'K': /* Kill user line on irc.conf */
- case 'k':
- aconf->status = CONF_KILL;
- break;
- /* Me. Host field is name used for this host */
- /* and port number is the number of the port */
- case 'M':
- case 'm':
- aconf->status = CONF_ME;
- break;
- case 'N': /* Server where I should NOT try to */
- case 'n': /* connect in case of lp failures */
- /* but which tries to connect ME */
- ++ncount;
- aconf->status = CONF_NOCONNECT_SERVER;
- break;
- case 'O':
- case 'o':
- aconf->status = CONF_OPERATOR;
- break;
- case 'P': /* listen port line */
- case 'p':
+ DupString (aconf->host, b_host[0] ? b_host : b_name);
+ DupString (aconf->passwd, b_passwd);
+ DupString (aconf->name, b_name[0] ? b_name : b_host);
+ aconf->port = b_port;
+ if (b_class)
+ Class (aconf) = find_class (b_class);
+ conf_add_item (aconf, opt);
+ }
+ else if (!mycmp (blocktype, "listen")) {
+ /* P:address:*:*:port */
+ aConfItem *aconf = make_conf ();
aconf->status = CONF_LISTEN_PORT;
- break;
- case 'Q': /* reserved nicks */
- aconf->status = CONF_QUARANTINED_NICK;
- break;
- case 'q': /* a server that you don't want in your */
- /* network. USE WITH CAUTION! */
- aconf->status = CONF_QUARANTINED_SERVER;
- break;
- case 'S': /* Service. Same semantics as */
- case 's': /* CONF_OPERATOR */
- aconf->status = CONF_SERVICE;
- break;
- case 'U': /* Underworld server, allowed to hack modes */
- case 'u': /* *Every* server on the net must define the same !!! */
- aconf->status = CONF_UWORLD;
- break;
- case 'Y':
- case 'y':
- aconf->status = CONF_CLASS;
- break;
- case 'Z':
- case 'z':
- aconf->status = CONF_ZAP;
- break;
- case 'X':
- case 'x':
+ DupString (aconf->host, b_host[0] ? b_host : "");
+ DupString (aconf->passwd, "*");
+ DupString (aconf->name, "*");
+ aconf->port = b_port;
+ conf_add_item (aconf, opt);
+ }
+ else if (!mycmp (blocktype, "oper")) {
+ /* O:host:passwd:nick:flags:class */
+ aConfItem *aconf = make_conf ();
+ aconf->status = CONF_OPERATOR;
+ DupString (aconf->host, b_host);
+ DupString (aconf->passwd, b_passwd);
+ DupString (aconf->name, b_name);
+ aconf->port = conf_parse_oper_flags (b_flags[0] ? b_flags : "*");
+ if (!(aconf->port & OFLAG_ISGLOBAL))
+ aconf->status = CONF_LOCOP;
+ if (b_class)
+ Class (aconf) = find_class (b_class);
+ conf_add_item (aconf, opt);
+ }
+ else if (!mycmp (blocktype, "drpass")) {
+ /* X:diepass:restartpass */
+ aConfItem *aconf = make_conf ();
aconf->status = CONF_DRPASS;
- break;
- default:
- Debug ((DEBUG_ERROR, "Error in config file: %s", line));
- break;
+ DupString (aconf->host, b_die);
+ DupString (aconf->passwd, b_restart);
+ DupString (aconf->name, "");
+ conf_add_item (aconf, opt);
}
- if (IsIllegal (aconf))
- continue;
-
- for (;;) { /* Parse remaining fields; break on missing field */
- /* X: line format is X:restartpass:diepass
- */
- if ((tmp = getfield (NULL)) == NULL)
- break;
- DupString (aconf->host, tmp);
- if ((tmp = getfield (NULL)) == NULL)
- break;
- DupString (aconf->passwd, tmp);
- if ((tmp = getfield (NULL)) == NULL)
- break;
- DupString (aconf->name, tmp);
- if ((tmp = getfield (NULL)) == NULL)
- break;
- if (aconf->status & CONF_OPS) {
- int *i, flag;
- char *m = "*";
- /*
- * Now we use access flags to define
- * what an operator can do with their O.
- */
- for (m = (*tmp) ? tmp : m; *m; m++) {
- for (i = oper_access; (flag = *i); i += 2)
- if (*m == (char) (*(i + 1))) {
- aconf->port |= flag;
- break;
- }
- }
- if (!(aconf->port & OFLAG_ISGLOBAL))
- aconf->status = CONF_LOCOP;
+ else if (!mycmp (blocktype, "connect")) {
+ /* Creates C-line + N-line, optionally H-line */
+ aConfItem *cconf = make_conf ();
+ aConfItem *nconf = make_conf ();
+ cconf->status = CONF_CONNECT_SERVER;
+ nconf->status = CONF_NOCONNECT_SERVER;
+ DupString (cconf->host, b_host);
+ DupString (cconf->passwd, b_passwd);
+ DupString (cconf->name, b_name);
+ cconf->port = b_port;
+ if (b_class)
+ Class (cconf) = find_class (b_class);
+ DupString (nconf->host, b_host);
+ DupString (nconf->passwd, b_passwd);
+ DupString (nconf->name, b_name);
+ nconf->port = 0;
+ if (b_class)
+ Class (nconf) = find_class (b_class);
+ conf_add_item (cconf, opt);
+ conf_add_item (nconf, opt);
+ if (b_hub[0]) {
+ aConfItem *hconf = make_conf ();
+ hconf->status = CONF_HUB;
+ DupString (hconf->host, b_hub);
+ DupString (hconf->passwd, "");
+ DupString (hconf->name, b_name);
+ conf_add_item (hconf, opt);
}
- else
- aconf->port = atoi (tmp);
- if ((tmp = getfield (NULL)) == NULL)
- break;
- Class (aconf) = find_class (atoi (tmp));
- break;
}
- /*
- ** If conf line is a general config, just
- ** see if we recognize the keyword, and set
- ** the appropriate global. We don't use a "standard"
- ** config link here, because these are things which need
- ** to be tested SO often that a simple global test
- ** is much better! -Aeto
- */
- if ((aconf->status & CONF_CONFIG) == CONF_CONFIG) {
- if (aconf->host && !mycmp (aconf->host, "hub"))
- is_hub = 1;
- continue;
+ else if (!mycmp (blocktype, "uworld")) {
+ /* U:name:*:* */
+ aConfItem *aconf = make_conf ();
+ aconf->status = CONF_UWORLD;
+ DupString (aconf->host, b_name);
+ DupString (aconf->passwd, "*");
+ DupString (aconf->name, "*");
+ conf_add_item (aconf, opt);
}
- /* Check if Z:line time -taz */
- if (aconf->status & CONF_ZTIME) {
- if ((aconf->host) && isdigit (*aconf->host))
- socks_zline_time = (atoi (aconf->host) * 60);
- continue;
+ else if (!mycmp (blocktype, "general")) {
+ if (b_hub_mode)
+ is_hub = 1;
+ if (b_zline_time)
+ socks_zline_time = b_zline_time * 60;
}
- /* Check for bad Z-lines masks as they are *very* dangerous
- if not correct!!! */
- if (aconf->status == CONF_ZAP) {
- char *tempc = aconf->host;
- if (!tempc) {
- free_conf (aconf);
- aconf = NULL;
- continue;
- }
- for (; *tempc; tempc++)
- if ((*tempc >= '0') && (*tempc <= '9'))
- goto zap_safe;
- free_conf (aconf);
- aconf = NULL;
- continue;
- zap_safe:;
+ else if (!mycmp (blocktype, "quarantine")) {
+ /* Q::reason:nick */
+ aConfItem *aconf = make_conf ();
+ aconf->status = CONF_QUARANTINED_NICK;
+ DupString (aconf->host, "");
+ DupString (aconf->passwd, b_passwd);
+ DupString (aconf->name, b_name);
+ conf_add_item (aconf, opt);
}
- /*
- ** If conf line is a class definition, create a class entry
- ** for it and make the conf_line illegal and delete it.
- */
- if (aconf->status & CONF_CLASS) {
- add_class (atoi (aconf->host), atoi (aconf->passwd),
- atoi (aconf->name), aconf->port, tmp ? atoi (tmp) : 0);
- continue;
+ else if (!mycmp (blocktype, "squar")) {
+ /* q::reason:servername */
+ aConfItem *aconf = make_conf ();
+ aconf->status = CONF_QUARANTINED_SERVER;
+ DupString (aconf->host, "");
+ DupString (aconf->passwd, b_passwd);
+ DupString (aconf->name, b_name);
+ conf_add_item (aconf, opt);
}
- /*
- ** associate each conf line with a class by using a pointer
- ** to the correct class record. -avalon
- */
- if (aconf->status & (CONF_CLIENT_MASK | CONF_LISTEN_PORT)) {
- if (Class (aconf) == 0)
- Class (aconf) = find_class (0);
- if (MaxLinks (Class (aconf)) < 0)
- Class (aconf) = find_class (0);
- }
- if (aconf->status & (CONF_LISTEN_PORT | CONF_CLIENT)) {
- aConfItem *bconf;
-
- if ((bconf = find_conf_entry (aconf, aconf->status))) {
- delist_conf (bconf);
- bconf->status &= ~CONF_ILLEGAL;
- if (aconf->status == CONF_CLIENT) {
- bconf->class->links -= bconf->clients;
- bconf->class = aconf->class;
- if (bconf->class)
- bconf->class->links += bconf->clients;
+ else if (!mycmp (blocktype, "kline")) {
+ /* K:host:reason:ident - one aConfItem per host */
+ int i;
+ if (b_host_count == 0) {
+ aConfItem *aconf = make_conf ();
+ aconf->status = CONF_KILL;
+ DupString (aconf->host, b_host);
+ DupString (aconf->passwd, b_passwd);
+ DupString (aconf->name, b_name[0] ? b_name : "*");
+ conf_add_item (aconf, opt);
+ } else {
+ for (i = 0; i < b_host_count; i++) {
+ aConfItem *aconf = make_conf ();
+ aconf->status = CONF_KILL;
+ DupString (aconf->host, b_hosts[i]);
+ DupString (aconf->passwd, b_passwd);
+ DupString (aconf->name, b_name[0] ? b_name : "*");
+ conf_add_item (aconf, opt);
}
- free_conf (aconf);
- aconf = bconf;
}
- else if (aconf->host && aconf->status == CONF_LISTEN_PORT)
- (void) add_listener (aconf);
}
- if (aconf->status & CONF_SERVER_MASK)
- if (ncount > MAXCONFLINKS || ccount > MAXCONFLINKS ||
- !aconf->host || strchr (aconf->host, '*') ||
- strchr (aconf->host, '?') || !aconf->name)
- continue;
-
- if (aconf->status & (CONF_SERVER_MASK | CONF_LOCOP | CONF_OPERATOR))
- if (!strchr (aconf->host, '@') && *aconf->host != '/') {
- char *newhost;
- int len = 3; /* *@\0 = 3 */
-
- len += strlen (aconf->host);
- newhost = (char *) MyMalloc (len);
- (void) sprintf (newhost, "*@%s", aconf->host);
- MyFree (aconf->host);
- aconf->host = newhost;
- }
- if (aconf->status & CONF_SERVER_MASK) {
- if (BadPtr (aconf->passwd))
- continue;
- else if (!(opt & BOOT_QUICK))
- (void) lookup_confhost (aconf);
+ else if (!mycmp (blocktype, "zline")) {
+ /* Z:address:reason:* */
+ aConfItem *aconf = make_conf ();
+ aconf->status = CONF_ZAP;
+ DupString (aconf->host, b_host);
+ DupString (aconf->passwd, b_passwd);
+ DupString (aconf->name, "*");
+ conf_add_item (aconf, opt);
}
- /*
- ** Own port and name cannot be changed after the startup.
- ** (or could be allowed, but only if all links are closed
- ** first).
- ** Configuration info does not override the name and port
- ** if previously defined. Note, that "info"-field can be
- ** changed by "/rehash".
- */
- if (aconf->status == CONF_ME) {
- strncpyzt (me.info, aconf->name, sizeof (me.info));
- if (me.name[0] == '\0' && aconf->host[0])
- strncpyzt (me.name, aconf->host, sizeof (me.name));
-
- if (aconf->passwd[0] && (aconf->passwd[0] != '*'))
- me.ip.s_addr = inet_addr (aconf->passwd);
- else
- me.ip.s_addr = INADDR_ANY;
- if (portnum < 0 && aconf->port >= 0)
- portnum = aconf->port;
- }
- if (aconf->status == CONF_KILL)
- aconf->tmpconf = KLINE_PERM;
- (void) collapse (aconf->host);
- (void) collapse (aconf->name);
- Debug ((DEBUG_NOTICE,
- "Read Init: (%d) (%s) (%s) (%s) (%d) (%d)",
- aconf->status, aconf->host, aconf->passwd,
- aconf->name, aconf->port, Class (aconf)));
- aconf->next = conf;
- conf = aconf;
- aconf = NULL;
+ else if (!mycmp (blocktype, "service")) {
+ /* S:hostmask:*:name */
+ aConfItem *aconf = make_conf ();
+ aconf->status = CONF_SERVICE;
+ DupString (aconf->host, b_host);
+ DupString (aconf->passwd, "*");
+ DupString (aconf->name, b_name);
+ if (b_class)
+ Class (aconf) = find_class (b_class);
+ conf_add_item (aconf, opt);
+ }
+ else if (!mycmp (blocktype, "dccblock")) {
+ /* E:filename:*:* */
+ aConfItem *aconf = make_conf ();
+ aconf->status = CONF_DCCBLOCK;
+ DupString (aconf->host, b_host[0] ? b_host : b_name);
+ DupString (aconf->passwd, "*");
+ DupString (aconf->name, "*");
+ conf_add_item (aconf, opt);
+ }
+ else
+ Debug ((DEBUG_ERROR, "Unknown block type '%s'", blocktype));
}
- if (aconf)
- free_conf (aconf);
- (void) dgets (-1, NULL, 0); /* make sure buffer is at empty pos */
- (void) close (fd);
+
+ MyFree (filebuf);
check_class ();
nextping = nextconnect = time (NULL);
return 0;
--- /dev/null
+#!/usr/bin/env python3
+"""
+Convert legacy colon-delimited ircd.conf to block-based format.
+
+Usage: python3 convert-conf.py < old-ircd.conf > new-ircd.conf
+
+This is a one-time migration tool. The new block format is the only
+format supported by the ircd going forward.
+"""
+
+import sys
+import re
+
+
+def parse_old_line(line):
+ """Parse a legacy colon-delimited config line into type and fields."""
+ line = line.rstrip('\n\r')
+ if not line or line.startswith('#') or line[0] in (' ', '\t'):
+ return None, None, line
+ if len(line) < 2 or line[1] != ':':
+ return None, None, line
+ linetype = line[0]
+ fields = line[2:].split(':')
+ return linetype, fields, line
+
+
+def quote_if_needed(s):
+ """Quote a string if it contains spaces or special characters."""
+ if not s:
+ return '""'
+ if ' ' in s or '\t' in s or '"' in s or ';' in s or '{' in s or '}' in s:
+ return '"' + s.replace('\\', '\\\\').replace('"', '\\"') + '"'
+ return s
+
+
+def convert_line(linetype, fields):
+ """Convert a parsed legacy line to block format string."""
+ # Pad fields to at least 5 elements
+ while len(fields) < 5:
+ fields.append('')
+
+ host = fields[0]
+ passwd = fields[1]
+ name = fields[2]
+ port_or_flags = fields[3]
+ classnum = fields[4]
+
+ if linetype == 'M':
+ lines = ['me {']
+ lines.append(f' name {quote_if_needed(host)};')
+ if passwd and passwd != '*':
+ lines.append(f' address {quote_if_needed(passwd)};')
+ else:
+ lines.append(' address *;')
+ lines.append(f' info {quote_if_needed(name)};')
+ if port_or_flags:
+ lines.append(f' port {port_or_flags};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'A':
+ # A:line1:line2:line3 (free-form admin info)
+ lines = ['admin {']
+ if host:
+ lines.append(f' line {quote_if_needed(host)};')
+ if passwd:
+ lines.append(f' line {quote_if_needed(passwd)};')
+ if name:
+ lines.append(f' line {quote_if_needed(name)};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'Y':
+ # Y:class:pingfreq:connfreq:maxlinks:sendq
+ lines = ['class {']
+ lines.append(f' name {host};')
+ if passwd:
+ lines.append(f' pingfreq {passwd};')
+ if name:
+ lines.append(f' connfreq {name};')
+ if port_or_flags:
+ lines.append(f' maxlinks {port_or_flags};')
+ if classnum:
+ lines.append(f' sendq {classnum};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'I':
+ # I:ipmask:password:hostmask:unused:class
+ lines = ['allow {']
+ lines.append(f' ip {quote_if_needed(host)};')
+ if name:
+ lines.append(f' host {quote_if_needed(name)};')
+ if passwd:
+ lines.append(f' password {quote_if_needed(passwd)};')
+ if classnum:
+ lines.append(f' class {classnum};')
+ if port_or_flags and port_or_flags != '0':
+ lines.append(f' port {port_or_flags};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'P':
+ # P:address:*:*:port
+ lines = ['listen {']
+ if host:
+ lines.append(f' address {quote_if_needed(host)};')
+ if port_or_flags:
+ lines.append(f' port {port_or_flags};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'O' or linetype == 'o':
+ # O:host:passwd:nick:flags:class
+ lines = ['oper {']
+ lines.append(f' host {quote_if_needed(host)};')
+ lines.append(f' password {quote_if_needed(passwd)};')
+ lines.append(f' name {quote_if_needed(name)};')
+ if port_or_flags:
+ lines.append(f' flags {port_or_flags};')
+ if classnum:
+ lines.append(f' class {classnum};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'X':
+ # X:diepass:restartpass
+ lines = ['drpass {']
+ lines.append(f' die {quote_if_needed(host)};')
+ lines.append(f' restart {quote_if_needed(passwd)};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'U':
+ # U:servername:*:*
+ lines = ['uworld {']
+ lines.append(f' name {quote_if_needed(host)};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'Q':
+ # Q::reason:nick
+ lines = ['quarantine {']
+ lines.append(f' nick {quote_if_needed(name)};')
+ if passwd:
+ lines.append(f' reason {quote_if_needed(passwd)};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'q':
+ # q::reason:server
+ lines = ['squar {']
+ lines.append(f' name {quote_if_needed(name)};')
+ if passwd:
+ lines.append(f' reason {quote_if_needed(passwd)};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'K' or linetype == 'k':
+ # K:host:reason:ident
+ lines = ['kline {']
+ lines.append(f' host {quote_if_needed(host)};')
+ if name:
+ lines.append(f' ident {quote_if_needed(name)};')
+ if passwd:
+ lines.append(f' reason {quote_if_needed(passwd)};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'Z' or linetype == 'z':
+ # Z:address:reason:*
+ lines = ['zline {']
+ lines.append(f' address {quote_if_needed(host)};')
+ if passwd:
+ lines.append(f' reason {quote_if_needed(passwd)};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'G' or linetype == 'g':
+ # G:option
+ if host and host.lower() == 'hub':
+ return 'general {\n hub yes;\n};'
+ return f'# Unrecognized G-line option: {host}'
+
+ elif linetype == 'F' or linetype == 'f':
+ # F:minutes
+ if host:
+ return f'general {{\n time {host};\n}};'
+ return ''
+
+ elif linetype == 'S' or linetype == 's':
+ # S:hostmask:*:name
+ lines = ['service {']
+ lines.append(f' host {quote_if_needed(host)};')
+ lines.append(f' name {quote_if_needed(name)};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'E' or linetype == 'e':
+ # E:filename:*:*
+ lines = ['dccblock {']
+ lines.append(f' name {quote_if_needed(host)};')
+ lines.append('};')
+ return '\n'.join(lines)
+
+ elif linetype == 'a':
+ # a: services admin - same as A but CONF_SADMIN
+ # Rarely used, just note it
+ return f'# Services admin (a-line): {host}:{passwd}:{name}'
+
+ else:
+ return None
+
+
+def main():
+ """Convert old ircd.conf from stdin to new format on stdout."""
+ # Collect C/N/H lines to merge into connect{} blocks
+ c_lines = {} # keyed by server name
+ n_lines = {}
+ h_lines = {} # keyed by server name
+ other_output = []
+ comments = []
+
+ input_lines = sys.stdin.readlines()
+
+ # First pass: collect C, N, H lines; convert everything else
+ for raw_line in input_lines:
+ linetype, fields, original = parse_old_line(raw_line)
+
+ if linetype is None:
+ # Comment or blank line
+ if original.startswith('#'):
+ comments.append(original)
+ else:
+ comments.append(original)
+ continue
+
+ # Flush accumulated comments before this block
+ if comments:
+ for c in comments:
+ other_output.append(c)
+ comments = []
+
+ while len(fields) < 5:
+ fields.append('')
+
+ if linetype in ('C', 'c'):
+ # C:host:passwd:servername:port:class
+ sname = fields[2]
+ c_lines[sname] = fields
+ elif linetype in ('N', 'n'):
+ # N:host:passwd:servername:unused:class
+ sname = fields[2]
+ n_lines[sname] = fields
+ elif linetype in ('H', 'h'):
+ # H:hubmask:unused:servername
+ sname = fields[2]
+ h_lines[sname] = fields
+ else:
+ result = convert_line(linetype, fields)
+ if result is not None:
+ other_output.append(result)
+ else:
+ other_output.append(f'# Unconverted: {original}')
+
+ # Flush trailing comments
+ if comments:
+ for c in comments:
+ other_output.append(c)
+
+ # Second pass: merge C/N/H into connect{} blocks
+ connect_blocks = []
+ all_servers = set(list(c_lines.keys()) + list(n_lines.keys()))
+ for sname in sorted(all_servers):
+ c = c_lines.get(sname)
+ n = n_lines.get(sname)
+ h = h_lines.get(sname)
+
+ lines = ['connect {']
+ # Prefer C-line fields, fall back to N-line
+ src = c if c else n
+ if src:
+ lines.append(f' host {quote_if_needed(src[0])};')
+ lines.append(f' password {quote_if_needed(src[1])};')
+ lines.append(f' name {quote_if_needed(src[2])};')
+ if c and c[3] and c[3] != '0':
+ lines.append(f' port {c[3]};')
+ else:
+ lines.append(' port 0;')
+ # Class from C-line or N-line
+ classnum = ''
+ if c and c[4]:
+ classnum = c[4]
+ elif n and n[4]:
+ classnum = n[4]
+ if classnum:
+ lines.append(f' class {classnum};')
+ if h:
+ lines.append(f' hub {quote_if_needed(h[0])};')
+ lines.append('};')
+ connect_blocks.append('\n'.join(lines))
+
+ # Output everything
+ print('# Converted from legacy colon-delimited format')
+ print('#')
+ print()
+ for item in other_output:
+ print(item)
+ if connect_blocks:
+ print()
+ print('# Server connections (merged from C/N/H lines)')
+ for block in connect_blocks:
+ print()
+ print(block)
+
+
+if __name__ == '__main__':
+ main()