Implement a progressive iauth system.
authorMichael Poole <mdpoole@troilus.org>
Thu, 16 Feb 2006 03:49:55 +0000 (03:49 +0000)
committerMichael Poole <mdpoole@troilus.org>
Thu, 16 Feb 2006 03:49:55 +0000 (03:49 +0000)
git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/branches/u2_10_12_branch@1620 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

27 files changed:
ChangeLog
doc/example.conf
doc/readme.features
doc/readme.iauth
doc/snomask.html
include/client.h
include/ircd_auth.h [deleted file]
include/ircd_features.h
include/s_auth.h
include/s_user.h
ircd/Makefile.in
ircd/ircd_auth.c [deleted file]
ircd/ircd_features.c
ircd/ircd_lexer.l
ircd/ircd_log.c
ircd/ircd_parser.y
ircd/list.c
ircd/m_cap.c
ircd/m_pass.c
ircd/m_pong.c
ircd/m_user.c
ircd/s_auth.c
ircd/s_conf.c
ircd/s_misc.c
ircd/s_stats.c
ircd/s_user.c
tools/iauth-test [new file with mode: 0755]

index 0067eb666e284eb303deefacc92bdd820057777d..8e6a0fdf77bdcfac34c469f71b538773919e9aae 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,85 @@
+2006-02-15  Michael Poole <mdpoole@troilus.org>
+
+       * doc/example.conf: Include new HIS_STATS_IAUTH feature.
+
+       * doc/readme.features: Document the feature.
+
+       * doc/readme.iauth: Rewrite to reflect the new progressive iauth
+       protocol, based on IRCnet's iauth.
+
+       * doc/snomask.html: Document SNO_AUTH server notice flag.
+
+       * include/client.h (FLAG_IAUTHED): Delete.
+       (con_cookie): Delete.
+       (con_unreg): Delete.
+       (con_auth): Make comment capitalization consistent.
+       (con_iauth): Delete.
+       (CLIREG_*): Delete.
+       (cli_unreg): Delete.
+       (cli_cookie): Delete.
+       (cli_iauth): Delete.
+       (con_unreg): Delete.
+       (con_iauth): Delete.
+       (IsIAuthed): Delete.
+       (SetIAuthed): Delete.
+       (SNO_AUTH): New server notice flag.
+       (SNO_ALL): Update to include SNO_AUTH.
+       (SNO_OPER): Update to include SNO_AUTH.
+
+       * include/ircd_auth.h: Delete file.
+
+       * include/ircd_features.h (HIS_STATS_IAUTH): New feature.
+
+       * include/s_auth.h: Rewrite almost everything for new auth system.
+
+       * include/s_user.h (COOKIE_VERIFIED): Delete.
+       (register_user): Remove redundant nick and username arguments.
+
+       * ircd/ircd_auth.c: Delete file.
+
+       * ircd/ircd_features.c (HIS_STATS_IAUTH): New feature.
+
+       * ircd/ircd_lexer.l (PROGRAM): New token in grammar.
+
+       * ircd/ircd_log.c (masks): Add SNO_AUTH flag.
+
+       * ircd/ircd_parser.y (stringlist): Simplify production.
+       (iauthblock): Revise to only include a PROGRAM production.
+
+       * ircd/list.c (make_client): Do not assign to deleted field.
+
+       * ircd/m_cap.c (cap_ls): Use auth_cap_start() instead of
+       cli_unreg().
+       (cap_req): Likewise.
+       (cap_end): Use auth_cap_done() instead of cli_unreg().
+
+       * ircd/m_pass.c (mr_pass): Merge arguments to PASS.  Use
+       auth_set_password() to notify iauth of password.
+
+       * ircd/m_pong.c (mr_pong): Use auth_set_pong() instead of
+       cli_cookie() and cli_unreg().
+
+       * ircd/m_user.c (m_user): Use auth_set_user() instead of
+       cli_unreg(), etc.
+
+       * ircd/s_auth.c: Rewrite most of the infrastructure for the new
+       auth system.
+
+       * ircd/s_conf.c (rehash): Call auth_*() instead of iauth_*().
+
+       * ircd/s_misc.c (exit_one_client): Do not use iauth_exit_client().
+       (exit_client): Use auth_send_exit() instead.
+
+       * ircd/s_stats.c (statsinfo): Include iauth and iauthconf.
+
+       * ircd/s_user.c (clean_user_id): Delete (moved into s_auth.c).
+       (register_user): Remove nick and username parameters; move conf
+       interactions and username validation to s_auth.c.
+       (set_nick_name): Use auth_set_nick() instead of cli_cookie(),
+       cli_unreg(), etc.
+
+       * tools/iauth-test: Implementation of iauth for testing purposes.
+
 2006-02-15  Michael Poole <mdpoole@troilus.org>
 
        * ircd/ircd_snprintf.c (doprintf): Fix typecast for %hu.
index 44eae6fb8161815fbe2e74cfb11aa5067dfbd58c..d90e0c092dde3cace41ad3206df007a371944c73 100644 (file)
@@ -845,6 +845,7 @@ features
 #  "HIS_STATS_x" = "TRUE";
 #  "HIS_STATS_y" = "TRUE";
 #  "HIS_STATS_z" = "TRUE";
+#  "HIS_STATS_IAUTH" = "TRUE";
 #  "HIS_WHOIS_SERVERNAME" = "TRUE";
 #  "HIS_WHOIS_IDLETIME" = "TRUE";
 #  "HIS_WHOIS_LOCALCHAN" = "TRUE";
index bef0e1c9552724e18a7f90a600a0072eacec6735..6412b941b0fecd6923c8b3a7d2ffbc9d8c7c6757 100644 (file)
@@ -654,6 +654,13 @@ HIS_STATS_z
 
 As per UnderNet CFV-165, this removes /STATS z from users.
 
+HIS_STATS_IAUTH
+ * Type: boolean
+ * Default: TRUE
+
+As per UnderNet CFV-165, this disables /STATS IAUTH and
+/STATS IAUTHCONF from users.
+
 HIS_WHOIS_SERVERNAME
  * Type: boolean
  * Default: TRUE
index 455d9da08b8547071a0f70a71c4d3586a8ffd60f..6aaec31ae3b600eceee152a99502479fc9f75792 100644 (file)
 OVERVIEW
 ========
 
-The IAUTH protocol used here is based on the one in ircd-hybrid 7.0,
-with minor changes to support login-on-connect and true IAUTH-side
-connection classes.  (Several networks use central authorities to vary
-per-netblock connection limits; for example, users from one ISP may
-only be allowed one connection per IP, or one shell provider's
-netblock may be limited to 50 total connections.)  IAUTH-side
-connection classes are controlled by a configuration option; if that
-is enabled, this document will say ICLASS is enabled.
-
-As in IRC, lines sent between the IRC and IAUTH servers are limited to
-512 characters, including the terminating <CR> <LF> sequence.  As in
-IRC, the final argument on a line may be prefixed with :, and must be
-prefixed with : if it contains a space (decimal 32) character.  Tokens
-are separated by single space characters, and each line is a separate
-command.  The first token on a line is a case-insensitive command
-name; unrecognized commands must be ignored.
-
-GREETING
-========
+The iauth protocol used here is based on the one in irc2.11.1, with
+minor changes to support challenge-response protocols and
+login-on-connect.  Reference to that version's iauth-internals.txt and
+source code may be useful.  For clarity, this document uses "server"
+to refer to any IRC server implementing this protocol, "ircu" to refer
+to Undernet ircd, and "ircd" to refer to IRCnet ircd.
+
+Certain messages are relayed to interested operators.  ircu implements
+this by using the 131072 (SNO_AUTH) server notice mask.  ircd
+implements this by using the &AUTH local channel.
+
+STARTING IAUTH
+==============
+
+The path to the iauth program is specified in the server configuration
+file.  The server spawns that program when reading the configuration
+file or when the previous iauth instance terminates.  To protect
+against a series of crashes, the server will refuse to restart an
+iauth instance that it spawned in the last five seconds.  A rehash
+operation will clear this behavior.  The server and iauth instance
+communicate over the iauth instance's stdin and stdout.
+
+Every message from the server to the iauth instance is a single line.
+The line starts with an integer client identifier.  This may be -1 to
+indicate no particular client or a non-negative number to indicate a
+client connected to the server.
+
+When the server starts the iauth instance, it sends a line formatted
+like "-1 M irc.example.org 20000" to indicate its name and an
+exclusive upper bound on valid client identifiers.  In that example,
+possible client identifiers would be from 0 through 19999 inclusive.
+This upper bound is called MAXCONNECTIONS in the server code.
+
+When the iauth instance starts, it sends a V message to indicate its
+version.
+
+The server should provide /stats subcommands that report the iauth
+instance's version, configuration and statistics.
+
+Line formats in both direction are IRC-like in format: space
+characters separate arguments and a colon at the start of an argument
+indicates that the remainder of the line is one argument.  To avoid
+problems, IPv6 address arguments with a leading colon may have to be
+prefixed with a 0 -- for example, ::1 sent as 0::1.
+
+When the iauth instance sends messages that relate to a particular
+client, that client is identified by three parameters from the
+server's Client Introduction message (<id>, <remoteip> and
+<remoteport>).  If any of these disagree with the server's current
+user tables, it is an error.
+
+CLIENT STATES
+=============
+
+Each client is conceptually in one of four states: GONE, REGISTER,
+HURRY or NORMAL.  Each client starts in the GONE state.  Certain
+messages from the server signal a client's transition from one state
+to another, and certain messages from the iauth instance cause a state
+transition.
+
+To be pedantic, the REGISTER state is a collection of sub-states since
+certain commands must occur at most and/or at least one time during
+the REGISTER state.  The distinctions between these sub-states are
+distracting and not important, so they are described as one state and
+the repetition limitations are described for each command.
+
+The rationale for the HURRY state is to give explicit input to the
+iauth instance as to when the server believes it has sent the complete
+set of data for the client.  Rather than defining the complete set of
+information in this protocol document, that is left to the server.
+ircd does not indicate this state.
+
+POLICIES AND USE CASES
+======================
+
+The historical application of iauth has been to block users that
+appear to be drones early, before they have a chance to disrupt the
+network, and without affecting other users on the same host (which
+K-lines do).  This protocol extends that application by adding the n
+server message and by allowing challenge-response exchanges with the
+client.
+
+Eventually it would be nice to move the DNS and ident lookups into
+iauth, and remove that code from the IRC server.  ircd already does
+this; since ircu does not, it adds the u server message.
+
+For trusted proxies, this protocol gives the capability for clients
+connecting through those proxies to be displayed with their actual
+username, IP address and hostname.  The same functions allow other
+clients to use iauth-assigned spoofs, for example to hide the IP
+addresses used by operators.
+
+This protocol allows login-on-connect, for example by clients that
+send their account name and password in PASS, through the R iauth
+message.
+
+This protocol allows iauth to assign a client to a particular class by
+specifying a class name in the D or R iauth message.
+
+SERVER MESSAGES
+===============
+
+X - Example Message Description
+Syntax: <id> X <several> <arguments>
+Example: 5 X arguments vary
+States: REGISTER(1), HURRY, NORMAL
+Next State: -
+Comments: This is an example message description.  Each message is a
+  single character.  The States field indicates which states the
+  message may occur in and any restrictions on how many times the
+  message may be sent during those states (restrictions only make
+  sense when Next State is -).  The Next State field indicates which
+  new state is implied by the message; a hyphen indicates no state
+  change is implied.  The X (Example) message is not a real message
+  type.
+Compatibility: If we believe ircu behavior is different than ircd's,
+  this describes ircd's behavior or expectations.
+
+C - Client Introduction
+Syntax: <id> C <remoteip> <remoteport> <localip> <localport>
+Example: 5 C 192.168.1.10 23367 192.168.0.1 6667
+States: GONE
+Next State: REGISTER
+Comments: Indicates that <localport> on <localip> accepted a client
+  connection from <remoteport> on <remoteip>.
+
+D - Client Disconnect
+Syntax: <id> D
+Example: 5 D
+States: REGISTER, HURRY, NORMAL
+Next State: GONE
+Comments: Indicates that a client is disconnecting from the server.
+
+N - Hostname Received
+Syntax: <id> N <hostname>
+Example: 5 N host-1-10.example.org
+States: REGISTER(1)
+Next State: -
+Comments: Indicates that the server received hostname information for
+  a client.  Only one of 'H' and 'd' is sent.
 
-The IRC server connects and sends the Server greeting:
-  Server <servername> [password]
-If ICLASS is enabled, it sends a list of currently connected users:
-  MyUsers <uid>:<username>@<hostname>:<ip> ...
-The IRC server may send several MyUsers lines.  When it has sent all
-MyUsers lines, it sends an EndUsers line:
-  EndUsers
-If ICLASS is disabled, EndUsers is sent immediately after Server.
+d - Hostname Timeout
+Syntax: <id> d
+Example: 5 d
+States: REGISTER(1)
+Next State: -
+Comments: Indicates that the server did not receive hostname
+  information for a client in a timely fashion.  Only one of 'H' and
+  'd' is sent.
 
-LOGIN REQUESTS
+P - Client Password
+Syntax: <id> P :<password ...>
+Example: 5 P :buddha n1rvan4
+States: REGISTER
+Next State: -
+Comments: Indicates the client's password information.  This may be a
+  traditional client password, an account and pass phrase pair, or the
+  response to a challenge (see the iauth C message).  This message is
+  enabled by requesting the A policy.
+
+U - Client Username
+Syntax: <id> U <username> :<userinfo ...>
+Example: 5 U buddha :Gautama Siddhartha
+States: REGISTER(1+)
+Next State: -
+Comments: Indicates the client's claimed username and "GECOS"
+  information.  This information is not reliable.  This message is
+  enabled by requesting the A policy.
+Compatibility: ircd does not include the <userinfo> data.
+
+u - Client Username
+Syntax: <id> u <username>
+Example: 5 u notbuddha
+States: REGISTER(1)
+Next State: -
+Comments: Indicates a more reliable username for the client.
+Compatibility: This is an Undernet extension and ircd does not send
+  it.  It is enabled by the iauth instance requesting the U policy.
+
+n - Client Nickname
+Syntax: <id> n <nickname>
+Example: 5 n Buddha
+States: REGISTER(1+), HURRY
+Next State: -
+Comments: Indicates the client's requested nickname.
+Compatibility: This is an Undernet extension and ircd does not send
+  it.  It is enabled by the iauth instance requesting the U policy.
+
+H - Hurry Up
+Syntax: <id> H <class>
+Example: 5 H Others
+States: REGISTER
+Next State: HURRY
+Comments: Indicates that the server is ready to register the client
+  except for needing a response from the iauth server.  <class> is
+  a tentative connection class for the user, which will be used unless
+  iauth overrides it in a D or R message.
+Compatibility: This is an Undernet extension and ircd does not send
+  it.  It is enabled by the iauth instance requesting the U policy.
+
+T - Client Registered
+Syntax: <id> T
+Example: 5 T
+States: HURRY
+Next State: NORMAL
+Comments: Indicates the server got tired of waiting for iauth to
+  finish and the client is being accepted.  This message should
+  never be sent when the R policy is in effect.
+Compatibility: ircd allows this message for clients in the REGISTER
+  state.
+
+E - Error
+Syntax: <id> E <type> :<additional text>
+Example: 5 E Gone
+States: N/A
+Next State: -
+Comments: Indicates that a message received from the iauth instance
+  could not be rationally interpreted.  This may be because the client
+  could not be found, the client was in an inappropriate state for the
+  message, or for other reasons.  The <type> argument specifies the
+  general type of error and <additional text> provides details.  <id>
+  may be -1.
+
+M - Server Name and Capacity
+Syntax: <id> M <servername> <capacity>
+Example: -1 M irc.example.org 20000
+States: GONE(1)
+Next State: -
+Comments: Indicates the server's name and upper bound on client
+  identifiers.
+Compatibility: ircd does not include the <capacity> information.
+  The <id> should be ignored: ircd sends 0 and ircu sends -1.
+
+IAUTH MESSAGES
 ==============
 
-When users connect, the IRC server sends a DoAuth request:
-  FullAuth <uid> <nickname> <username> <hostname> <ip> <account> <password> <realname>
-<uid> is a text string up to 20 characters long that identifies the
-client, and is unique a BadAuth response is received or until an
-ExitUser command is sent with the same uid (see below for details on
-those messages).  <uid> may not contain a colon character.  <nickname>
-is the client's initially requested nickname.  <username> is the
-username returned by the ident server (RFC 1413), or a tilde-prefixed
-username supplied by the user.  <hostname> is a text hostname,
-possibly in the form of a dotted quad or IPv6 address, or the
-character '?'.  <ip> is a dotted quad IPv4 address or an IPv6 hex
-address.  <account> and <password> are optional, and are used when the
-client attempts login-on-connect.  <realname> is the realname
-specified by the client's USER message, and may contain spaces.
-
-If the client is accepted, the IAUTH server responds:
-  DoneAuth <uid> <username> <hostname> <class> [account]
-<username> is a replacement username, and <hostname> is a replacement
-hostname.  If the <hostname> from DoAuth was ?, <hostname> is the
-result of a DNS lookup for the client.  <class> is the name of a
-connection class for the client.  <account> is optional and is
-provided if the user's login was successful.
-
-If the client is rejected, the IAUTH server responds:
-  BadAuth <uid> :<reason>
-<reason> may include spaces, and should have a leading ':' sentinel.
-
-DISCONNECTS
-===========
-
-If ICLASS is enabled, the IRC server sends ExitUser when a client
-disconnects:
-  ExitUser <uid>
-
-DIFFERENCES FROM IRCD-HYBRID
-============================
-
-The ircd-hybrid IAUTH code is slightly bitrotted and disabled in 7.0
-(through at least 7.0.1).  This code added the following items:
-  MyUsers, EndUsers and ExitUser commands
-  Server passwords may contain whitespace and be prefixed by :
-  DoneAuth may include an account name
-  FullAuth command replaces DoAuth command and adds account, password,
-    realname parameters
-The Class command is present in ircd-hybrid's code but not used here.
-IP addresses in ircd-hybrid are "in unsigned int format," which is
-limited to IPv4, and so it is not used here.
+X - Example Message Description
+Syntax: X <arguments>
+Example: X something
+Notify: yes
+States: N/A
+Next State: N/A
+Comments: This is an example message description.  Each message is a
+  single character.  If the Notify field is present and says yes,
+  interested operators (with SNO_AUTH set) should be notified of the
+  message.  The States field, where present, indicate which states
+  accept this message.  Clients in other states should ignore the
+  message or treat it as an error.  The Next State field, where
+  present, indicates what the next state should be for the client.
+Compatibility: If we believe ircu behavior is different than ircd's,
+  this describes ircd's behavior or expectations.
+
+> - Operator Notification
+Syntax: > :<message text>
+Example: > :Hello Operators!
+Notify: yes
+Comments: Contains a message that the iauth instance wants to send to
+  interested operators.
+
+G - Set Debug Level
+Syntax: G <level>
+Example: G 1
+Notify: yes
+Comments: Sets a debug level for the server's end of the iauth
+  conversation.  When enabled, debug messages should be sent to the
+  same channel (group, mask, etc) as other iauth notifications.
+  Debug level 0 suppresses iauth-related debug output, and positive
+  integers enable iauth debugging messages.
+
+O - Set Policy Options
+Syntax: O <options>
+Example: O RTAWU
+Notify: yes
+Comments: Sets policy options for the iauth conversation.  Old policy
+  options should be forgotten.  Valid policy options are:
+   A - Send username and password information.
+       This causes the server to send the U and P messages.
+   R - Require clients to be approved before registering them.
+       When this policy is in effect, it affects the behavior
+       of a registration timeout; for details, see the documentation
+       for the T server message.
+   T - When the R policy is in effect and the iauth service does not
+       respond for a client, this causes the server to count the number
+       of clients refused, to send a warning message to interested
+       operators periodically, and to send the count of rejected users
+       to interested operators when the iauth instance responds again.
+   U - Send nickname, confirmed username and hurry information.
+       This causes the server to send the n, u and H messages.
+   W - Allow extra time for iauth to respond based on hostname.
+       When this policy is in effect and a DNS message (N or d) is
+       sent for a client, that client's registration timeout is
+       extended or reset.
+Compatibility: The U policy is an Undernet extension and is not
+  recognized by ircd.
+
+V - iauth Program Version
+Syntax: V :<version string>
+Example: V :Undernet-iauthu v1.0
+Notify: yes
+Comments: Indicates the iauth program version.  This should only be
+  used in diagnostic messages, and must not change protocol behavior.
+
+a - Start of new configuration
+Syntax: a
+Example: a
+Notify: yes
+Comments: Indicates that a new configuration is being loaded by the
+  iauth instance.  Any cached configuration records should be cleared.
+
+A - Configuration Information
+Syntax: A <hosts?> <module> :<options>
+Example: A * rfc931
+Notify: yes
+Comments: Indicates new configuration information.
+
+s - Start of new statistics
+Syntax: s
+Example: s
+Notify: yes
+Comments: Indicates a new set of statistics will be sent.  Any cached
+  statistics records should be cleared.
+
+S - Statistics Information
+Syntax: S <module> :<module information>
+Example: S rfc931 connected 0 unix 0 other 0 bad 0 out of 0
+Notify: yes
+Comments: Indicates new or additional statistics information.
+
+o - Forced Username
+Syntax: o <id> <remoteip> <remoteport> <username>
+Example: o 5 192.168.1.10 23367 bubba
+States: REGISTER, HURRY
+Next State: -
+Comments: Indicates that the username should be used for the specified
+  client even if the normal sanity-checking would prohibit the
+  username.
+
+U - Trusted Username
+Syntax: U <id> <remoteip> <remoteport> <username>
+Example: U 5 192.168.1.10 23367 buddha
+States: REGISTER, HURRY
+Next State: -
+Comments: Indicates that the iauth instance believes <username> is
+  accurate for the specified client.
+
+u - Untrusted Username
+Syntax: u <id> <remoteip> <remoteport> <username>
+Example: u 5 192.168.1.10 23367 enlightened_one
+States: REGISTER, HURRY
+Next State: -
+Comments: Indicates that the iauth instance does not strongly trust
+  <username> to be accurate, but has no more trusted username.
+
+N - Client Hostname
+Syntax: N <id> <remoteip> <remoteport> <hostname>
+Example: N 5 192.168.1.10 23367 buddha.example.org
+States: REGISTER, HURRY
+Next State: -
+Comments: Indicates that the iauth instance believes the specified
+  client should use the hostname given.
+Compatibility: This is an Undernet extension and ircd does not support
+  this message.
+
+I - Client IP Address
+Syntax: N <id> <currentip> <remoteport> <newip>
+Example: N 5 192.168.1.10 23367 127.128.129.130
+States: REGISTER, HURRY
+Next State: -
+Comments: Indicates that the iauth instance wants the server to
+  present and treat the client as using <newip>.  This means that
+  future iauth messages relating to the client must use <newip>
+  as the <remoteip> parameter.
+Compatibility: This is an Undernet extension and ircd does not support
+  this message.
+
+C - Challenge User
+Syntax: C <id> <remoteip> <remoteport> :<challenge string>
+Example: C 5 192.168.1.10 23367 :In which year did Columbus sail the ocean blue?
+States: REGISTER, HURRY
+Next State: -
+Comments: Indicates that the challenge string should be sent to the
+  specified user, for example via NOTICE AUTH :*** <challenge string>.
+  The client responds by sending PASS :<response>, which should be
+  relayed via the P server message.  This requires that the A policy
+  be in effect.
+Compatibility: This is an Undernet extension and ircd does not support
+  this message.
+
+k - Quietly Kill Client
+Syntax: k <id> <remoteip> <remoteport> :<reason>
+Example: k 5 192.168.1.10 23367 :Open proxy found.
+States: REGISTER, HURRY, NORMAL
+Next State: GONE
+Comments: Indicates that the specified client should be disconnected
+  for the reason given without notifying operators.
+Compatibility: ircu does not use the same notification mechanism as
+  ircd, so operators are notified using SNO_CONNEXIT anyway.
+
+K - Kill Client
+Syntax: K <id> <remoteip> <remoteport> :<reason>
+Example: K 5 192.168.1.10 23367 :We don't like you.
+States: REGISTER, HURRY, NORMAL
+Next State: GONE
+Comments: Indicates that the specified client should be disconnected
+  for the reason given.  Operators should be notified.
+
+D - Done Checking
+Syntax: D <id> <remoteip> <remoteport> [class]
+Example: D 5 192.168.1.10 23367
+States: REGISTER, HURRY
+Next State: NORMAL
+Comments: Indicates that the iauth instance believes the specified
+  client should be allowed onto the network.  If a class parameter is
+  given, the client should be assigned to that class.
+Compatibility: Specifying the class is an Undernet extension and ircd
+  does not support that parameter.
+
+R - Registered User
+Syntax: R <id> <remoteip> <remoteport> <account> [class]
+Example: R 5 192.168.1.10 23367 Buddha
+States: REGISTER, HURRY
+Next State: NORMAL
+Comments: Indicates that the iauth instance believes the specified
+  client should be allowed onto the network, pre-authenticated to
+  the account listed.  If a class parameter is given, the client
+  should be assigned to that class.
+Compatibility: This is an Undernet extension and ircd does not support
+  this message.
index 8abc768a112e2a4af0db3cab5ebc627c26b0f3dd..237a3bef71e66df9d7b63e2cee8216a4531ffb4c 100644 (file)
@@ -137,6 +137,12 @@ Usage:
 <td><font face="arial" size="2">0x10000</td>\r
 <td><font face="arial" size="2">/* debugging messages (DEBUGMODE only) */</td>\r
 </tr>\r
+<tr>\r
+<td><font face="arial" size="2">131072</td>\r
+<td><font face="arial" size="2">SNO_AUTH</td>\r
+<td><font face="arial" size="2">0x20000</td>\r
+<td><font face="arial" size="2">/* iauth status messages */</td>\r
+</tr>\r
 </table>\r
 </center>\r
 \r
@@ -153,7 +159,7 @@ Usage:
 </tr>\r
 <tr>\r
 <td><font face="arial" size="2">only opers may set</td>\r
-<td><font face="arial" size="2">SNO_OPER (SNO_CONNEXIT | SNO_OLDREALOP)</td>\r
+<td><font face="arial" size="2">SNO_OPER (SNO_CONNEXIT | SNO_OLDREALOP | SNO_AUTH)</td>\r
 </tr>\r
 </table>\r
 \r
index 6e23e42361433855c1778b5e6f1fd49d01bb6920..aea930ab404da257135bb8b298e07cb618e0120c 100644 (file)
@@ -155,7 +155,6 @@ enum Flag
     FLAG_BURST,                     /**< Server is receiving a net.burst */
     FLAG_BURST_ACK,                 /**< Server is waiting for eob ack */
     FLAG_IPCHECK,                   /**< Added or updated IPregistry data */
-    FLAG_IAUTHED,                   /**< Got IAUTH response for user */
     FLAG_LOCOP,                     /**< Local operator -- SRB */
     FLAG_SERVNOTICE,                /**< server notices such as kill */
     FLAG_OPER,                      /**< Operator */
@@ -198,7 +197,6 @@ struct Connection
   time_t              con_nexttarget;/**< Next time a target change is allowed */
   time_t              con_lasttime;  /**< Last time data read from socket */
   time_t              con_since;     /**< Last time we accepted a command */
-  unsigned int        con_cookie;    /**< Random number the user must PONG */
   struct MsgQ         con_sendQ;     /**< Outgoing message queue */
   struct DBuf         con_recvQ;     /**< Incoming data yet to be parsed */
   unsigned int        con_sendM;     /**< Stats: protocol messages sent */
@@ -211,7 +209,6 @@ struct Connection
   HandlerType         con_handler;   /**< Message index into command table
                                         for parsing. */
   struct ListingArgs* con_listing;   /**< Current LIST status. */
-  unsigned long       con_unreg;     /**< Indicate what still needs to be done */
   unsigned int        con_max_sendq; /**< cached max send queue for client */
   unsigned int        con_ping_freq; /**< cached ping freq */
   unsigned short      con_lastsq;    /**< # 2k blocks when sendqueued
@@ -233,8 +230,7 @@ struct Connection
   struct Privs        con_privs;     /**< Oper privileges */
   struct CapSet       con_capab;     /**< Client capabilities (from us) */
   struct CapSet       con_active;    /**< Active capabilities (to us) */
-  struct AuthRequest* con_auth;      /**< auth request for client */
-  struct IAuthRequest* con_iauth;    /**< iauth request for client */
+  struct AuthRequest* con_auth;      /**< Auth request for client */
 };
 
 /** Magic constant to identify valid Connection structures. */
@@ -264,13 +260,6 @@ struct Client {
   char cli_info[REALLEN + 1];     /**< Free form additional client information */
 };
 
-#define CLIREG_NICK    0x0001  /**< Client must set nickname */
-#define CLIREG_USER    0x0002  /**< Client must set username */
-#define CLIREG_COOKIE  0x0004  /**< Client must return cookie */
-#define CLIREG_CAP     0x0008  /**< Client in capability negotiation */
-
-#define CLIREG_INIT    (CLIREG_NICK | CLIREG_USER | CLIREG_COOKIE)
-
 /** Magic constant to identify valid Client structures. */
 #define CLIENT_MAGIC 0x4ca08286
 
@@ -322,8 +311,6 @@ struct Client {
 #define cli_capab(cli)         con_capab(cli_connect(cli))
 /** Get active client capabilities for client */
 #define cli_active(cli)                con_active(cli_connect(cli))
-/** Get flags for remaining registration tasks */
-#define cli_unreg(cli)         con_unreg(cli_connect(cli))
 /** Get client name. */
 #define cli_name(cli)          ((cli)->cli_name)
 /** Get client username (ident). */
@@ -345,8 +332,6 @@ struct Client {
 #define cli_nextnick(cli)      con_nextnick(cli_connect(cli))
 /** Get next time a target change is allowed for the client. */
 #define cli_nexttarget(cli)    con_nexttarget(cli_connect(cli))
-/** Get required PING/PONG cookie for client. */
-#define cli_cookie(cli)                con_cookie(cli_connect(cli))
 /** Get SendQ for client. */
 #define cli_sendQ(cli)         con_sendQ(cli_connect(cli))
 /** Get RecvQ for client. */
@@ -389,8 +374,6 @@ struct Client {
 #define cli_proc(cli)          con_proc(cli_connect(cli))
 /** Get auth request for client. */
 #define cli_auth(cli)          con_auth(cli_connect(cli))
-/** Get iauth request for client. */
-#define cli_iauth(cli)          con_iauth(cli_connect(cli))
 /** Get sentalong marker for client. */
 #define cli_sentalong(cli)      con_sentalong(cli_connect(cli))
 
@@ -424,8 +407,6 @@ struct Client {
 #define con_lasttime(con)       ((con)->con_lasttime)
 /** Get last time we accepted a command from the connection. */
 #define con_since(con)          ((con)->con_since)
-/** Get PING/PONG confirmation cookie for connection. */
-#define con_cookie(con)                ((con)->con_cookie)
 /** Get SendQ for connection. */
 #define con_sendQ(con)         ((con)->con_sendQ)
 /** Get RecvQ for connection. */
@@ -446,8 +427,6 @@ struct Client {
 #define con_handler(con)       ((con)->con_handler)
 /** Get the LIST status for the connection. */
 #define con_listing(con)       ((con)->con_listing)
-/** Get remining steps before registration completes. */
-#define con_unreg(con)          ((con)->con_unreg)
 /** Get the maximum permitted SendQ size for the connection. */
 #define con_max_sendq(con)     ((con)->con_max_sendq)
 /** Get the ping frequency for the connection. */
@@ -476,8 +455,6 @@ struct Client {
 #define con_active(con)         (&(con)->con_active)
 /** Get the auth request for the connection. */
 #define con_auth(con)          ((con)->con_auth)
-/** Get the iauth request for the connection. */
-#define con_iauth(con)          ((con)->con_iauth)
 
 #define STAT_CONNECTING         0x001 /**< connecting to another server */
 #define STAT_HANDSHAKE          0x002 /**< pass - server sent */
@@ -572,8 +549,6 @@ struct Client {
 #define IsDeaf(x)               HasFlag(x, FLAG_DEAF)
 /** Return non-zero if the client has been IP-checked for clones. */
 #define IsIPChecked(x)          HasFlag(x, FLAG_IPCHECK)
-/** Return non-zero if the client has been okayed by iauth. */
-#define IsIAuthed(x)            HasFlag(x, FLAG_IAUTHED)
 /** Return non-zero if we have received an ident response for the client. */
 #define IsIdented(x)            HasFlag(x, FLAG_GOTID)
 /** Return non-zero if the client has set mode +i (invisible). */
@@ -626,8 +601,6 @@ struct Client {
 #define SetGotId(x)             SetFlag(x, FLAG_GOTID)
 /** Mark a client as being IP-checked. */
 #define SetIPChecked(x)         SetFlag(x, FLAG_IPCHECK)
-/** Mark a client as being iauth-checked. */
-#define SetIAuthed(x)           SetFlag(x, FLAG_IAUTHED)
 /** Mark a client as having mode +i (invisible). */
 #define SetInvisible(x)         SetFlag(x, FLAG_INVISIBLE)
 /** Mark a client as causing a net.join. */
@@ -716,12 +689,13 @@ struct Client {
 #define SNO_CONNEXIT    0x4000  /**< client connect/exit (ugh) */
 #define SNO_AUTO        0x8000  /**< AUTO G-Lines */
 #define SNO_DEBUG       0x10000 /**< debugging messages (DEBUGMODE only) */
+#define SNO_AUTH        0x20000 /**< IAuth notices */
 
+/** Bitmask of all valid server notice bits. */
 #ifdef DEBUGMODE
-# define SNO_ALL        0x1ffff  /**< Bitmask of all valid server
-                                  * notice bits. */
+# define SNO_ALL        0x3ffff
 #else
-# define SNO_ALL        0xffff
+# define SNO_ALL        0x2ffff
 #endif
 
 /** Server notice bits allowed to normal users. */
@@ -732,7 +706,7 @@ struct Client {
 /** Server notice bits enabled by default for IRC operators. */
 #define SNO_OPERDEFAULT (SNO_DEFAULT|SNO_HACK2|SNO_HACK4|SNO_THROTTLE|SNO_OLDSNO)
 /** Server notice bits reserved to IRC operators. */
-#define SNO_OPER (SNO_CONNEXIT|SNO_OLDREALOP)
+#define SNO_OPER (SNO_CONNEXIT|SNO_OLDREALOP|SNO_AUTH)
 /** Noisy server notice bits that cause other bits to be cleared during connect. */
 #define SNO_NOISY (SNO_SERVKILL|SNO_UNAUTH)
 
diff --git a/include/ircd_auth.h b/include/ircd_auth.h
deleted file mode 100644 (file)
index 9d36764..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef INCLUDED_ircd_auth_h
-#define INCLUDED_ircd_auth_h
-
-/*
- * IRC - Internet Relay Chat, ircd/ircd_auth.h
- * Copyright 2004 Michael Poole <mdpoole@troilus.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-/** @file
- * @brief Interface to IAuth client implementation.
- * @version $Id$
- */
-
-#ifndef INCLUDED_config_h
-#include "config.h"
-#endif
-
-struct IAuth;
-extern struct IAuth *iauth_active;
-
-struct IAuth *iauth_connect(char *host, unsigned short port, char *passwd, time_t reconnect, time_t timeout);
-int iauth_start_client(struct IAuth *iauth, struct Client *cptr);
-void iauth_exit_client(struct Client *cptr);
-
-void iauth_mark_closing(void);
-void iauth_close_unused(void);
-
-#endif
index 524f23a7a7daab951c2aa3a21d3781ae6f8d299d..737ffc92a7e4a3879614cffece8c677f55e8b145 100644 (file)
@@ -135,6 +135,7 @@ enum Feature {
   FEAT_HIS_STATS_x,
   FEAT_HIS_STATS_y,
   FEAT_HIS_STATS_z,
+  FEAT_HIS_STATS_IAUTH,
   FEAT_HIS_WHOIS_SERVERNAME,
   FEAT_HIS_WHOIS_IDLETIME,
   FEAT_HIS_WHOIS_LOCALCHAN,
index 2925339e0079bfe679e9db8d960595a8dcfcd2a3..6e2a067049d9eae8130c725c57e07320c0c0755f 100644 (file)
 #endif
 
 struct Client;
-
-/** Stores state of the DNS and RFC 1413 ident lookups for a client. */
-struct AuthRequest {
-  struct AuthRequest* next;      /**< linked list node ptr */
-  struct AuthRequest* prev;      /**< linked list node ptr */
-  struct Client*      client;    /**< pointer to client struct for request */
-  unsigned int        flags;     /**< current state of request */
-  int                 fd;        /**< file descriptor for auth queries */
-  struct Socket       socket;    /**< socket descriptor for auth queries */
-  struct Timer        timeout;   /**< timeout timer for auth queries */
-};
-
-/*
- * flag values for AuthRequest
- * NAMESPACE: AM_xxx - Authentication Module
- */
-#define AM_AUTH_CONNECTING   0x01 /**< waiting for ident connect to complete */
-#define AM_AUTH_PENDING      0x02 /**< ident connected, waiting for response */
-#define AM_DNS_PENDING       0x04 /**< dns request sent, waiting for response */
-
-#define AM_SOCKET            0x40 /**< socket structure not destroyed */
-#define AM_TIMEOUT           0x80 /**< timer structure not destroyed */
-
-/** If any of AM_FREE_MASK bits are set, operations are still in progress. */
-#define AM_FREE_MASK         (AM_SOCKET | AM_TIMEOUT)
-
-#define SetDNSPending(x)     ((x)->flags |= AM_DNS_PENDING)
-#define ClearDNSPending(x)   ((x)->flags &= ~AM_DNS_PENDING)
-#define IsDNSPending(x)      ((x)->flags &  AM_DNS_PENDING)
-
-#define SetAuthConnect(x)    ((x)->flags |= AM_AUTH_CONNECTING)
-#define ClearAuthConnect(x)  ((x)->flags &= ~AM_AUTH_CONNECTING)
-#define IsAuthConnect(x)     ((x)->flags &  AM_AUTH_CONNECTING)
-
-#define SetAuthPending(x)    ((x)->flags |= AM_AUTH_PENDING)
-#define ClearAuthPending(x)  ((x)->flags &= AM_AUTH_PENDING)
-#define IsAuthPending(x)     ((x)->flags &  AM_AUTH_PENDING)
-
-#define ClearAuth(x)         ((x)->flags &= ~(AM_AUTH_PENDING | AM_AUTH_CONNECTING))
-#define IsDoingAuth(x)       ((x)->flags &  (AM_AUTH_PENDING | AM_AUTH_CONNECTING))
+struct AuthRequest;
+struct StatDesc;
 
 extern void start_auth(struct Client *);
-extern void read_auth_reply(struct AuthRequest* req);
-extern void send_auth_query(struct AuthRequest* req);
+extern int auth_set_pong(struct AuthRequest *auth, unsigned int cookie);
+extern int auth_set_user(struct AuthRequest *auth, const char *username, const char *userinfo);
+extern int auth_set_nick(struct AuthRequest *auth, const char *nickname);
+extern int auth_set_password(struct AuthRequest *auth, const char *password);
+extern int auth_cap_start(struct AuthRequest *auth);
+extern int auth_cap_done(struct AuthRequest *auth);
 extern void destroy_auth_request(struct AuthRequest *req, int send_reports);
 
+extern int auth_spawn(int argc, char *argv[]);
+extern void auth_send_exit(struct Client *cptr);
+extern void auth_mark_closing(void);
+extern void auth_close_unused(void);
+extern void report_iauth_conf(struct Client *cptr, const struct StatDesc *sd, char *param);
+extern void report_iauth_stats(struct Client *cptr, const struct StatDesc *sd, char *param);
+
 #endif /* INCLUDED_s_auth_h */
 
index a395f3511f04dad6db1299f97c49d536a33e289f..205776c2d9321e6db886d28e94ace31d290f858f 100644 (file)
@@ -50,8 +50,6 @@ struct Flags;
 #define MATCH_SERVER  1 /**< flag for relay_masked_message (etc) to indicate the mask matches a server name */
 #define MATCH_HOST    2 /**< flag for relay_masked_message (etc) to indicate the mask matches host name */
 
-#define COOKIE_VERIFIED 0xffffffff /**< value for cli_cookie() to show completion */
-
 /** Formatter function for send_user_info().
  * @param who Client being displayed.
  * @param sptr Client requesting information.
@@ -64,8 +62,7 @@ typedef void (*InfoFormatter)(struct Client* who, struct Client *sptr, struct Ms
  */
 extern struct User* make_user(struct Client *cptr);
 extern void         free_user(struct User *user);
-extern int          register_user(struct Client* cptr, struct Client* sptr,
-                                  const char* nick, char* username);
+extern int          register_user(struct Client* cptr, struct Client *sptr);
 
 extern void         user_count_memory(size_t* count_out, size_t* bytes_out);
 
index f0a96ebe6fe2e7efedd728c548160f3c1affdba0..22c5a06196e2424130bde6e1352f7ea68127be62 100644 (file)
@@ -98,7 +98,6 @@ IRCD_SRC = \
        hash.c \
        ircd.c \
        ircd_alloc.c \
-       ircd_auth.c \
        ircd_crypt.c \
        ircd_events.c \
        ircd_features.c \
@@ -458,17 +457,6 @@ ircd.o: ircd.c ../config.h ../include/ircd.h ../include/struct.h \
 ircd_alloc.o: ircd_alloc.c ../config.h ../include/ircd_alloc.h \
   ../include/ircd_log.h ../include/ircd_string.h ../include/ircd_chattr.h \
   ../include/s_debug.h ../config.h ../include/ircd_defs.h
-ircd_auth.o: ircd_auth.c ../config.h ../include/client.h \
-  ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
-  ../include/ircd_events.h ../config.h ../include/ircd_handler.h \
-  ../include/res.h ../include/capab.h ../include/ircd_alloc.h \
-  ../include/ircd_auth.h ../include/ircd_events.h \
-  ../include/ircd_features.h ../include/ircd_log.h \
-  ../include/ircd_osdep.h ../include/ircd_snprintf.h \
-  ../include/ircd_string.h ../include/ircd_chattr.h ../include/ircd.h \
-  ../include/struct.h ../include/msg.h ../include/msgq.h ../include/res.h \
-  ../include/s_bsd.h ../include/s_debug.h ../include/s_misc.h \
-  ../include/s_user.h ../include/send.h
 ircd_crypt.o: ircd_crypt.c ../config.h ../include/ircd_crypt.h \
   ../include/ircd_alloc.h ../include/ircd_features.h \
   ../include/ircd_log.h ../include/ircd_string.h ../include/ircd_chattr.h \
@@ -534,11 +522,11 @@ ircd_reslib.o: ircd_reslib.c ../include/ircd.h ../include/struct.h \
   ../include/ircd_reslib.h ../include/ircd_defs.h ../include/fileio.h \
   ../include/ircd_string.h ../include/ircd_chattr.h
 ircd_signal.o: ircd_signal.c ../config.h ../include/ircd.h \
-  ../include/struct.h ../include/ircd_defs.h ../include/ircd_events.h \
-  ../config.h ../include/ircd_log.h ../include/ircd_signal.h \
-  ../include/s_conf.h ../include/client.h ../include/dbuf.h \
-  ../include/msgq.h ../include/ircd_handler.h ../include/res.h \
-  ../include/capab.h
+  ../include/struct.h ../include/ircd_defs.h ../include/ircd_alloc.h \
+  ../include/ircd_events.h ../config.h ../include/ircd_log.h \
+  ../include/ircd_signal.h ../include/s_conf.h ../include/client.h \
+  ../include/dbuf.h ../include/msgq.h ../include/ircd_handler.h \
+  ../include/res.h ../include/capab.h
 ircd_snprintf.o: ircd_snprintf.c ../config.h ../include/client.h \
   ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
   ../include/ircd_events.h ../config.h ../include/ircd_handler.h \
@@ -633,7 +621,8 @@ m_cap.o: m_cap.c ../config.h ../include/client.h ../include/ircd_defs.h \
   ../include/capab.h ../include/ircd.h ../include/struct.h \
   ../include/ircd_chattr.h ../include/ircd_log.h ../include/ircd_reply.h \
   ../include/ircd_snprintf.h ../include/ircd_string.h ../include/msg.h \
-  ../include/numeric.h ../include/send.h ../include/s_user.h
+  ../include/numeric.h ../include/send.h ../include/s_auth.h \
+  ../include/s_user.h
 m_clearmode.o: m_clearmode.c ../config.h ../include/client.h \
   ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
   ../include/ircd_events.h ../config.h ../include/ircd_handler.h \
@@ -780,7 +769,8 @@ m_join.o: m_join.c ../config.h ../include/channel.h \
   ../include/ircd_chattr.h ../include/ircd_features.h \
   ../include/ircd_log.h ../include/ircd_reply.h ../include/ircd_string.h \
   ../include/msg.h ../include/numeric.h ../include/numnicks.h \
-  ../include/s_debug.h ../include/s_user.h ../include/send.h
+  ../include/s_debug.h ../include/s_user.h ../include/send.h \
+  ../include/sys.h
 m_jupe.o: m_jupe.c ../config.h ../include/client.h ../include/ircd_defs.h \
   ../include/dbuf.h ../include/msgq.h ../include/ircd_events.h \
   ../config.h ../include/ircd_handler.h ../include/res.h \
@@ -920,7 +910,8 @@ m_pass.o: m_pass.c ../config.h ../include/client.h ../include/ircd_defs.h \
   ../include/dbuf.h ../include/msgq.h ../include/ircd_events.h \
   ../config.h ../include/ircd_handler.h ../include/res.h \
   ../include/capab.h ../include/ircd_log.h ../include/ircd_reply.h \
-  ../include/ircd_string.h ../include/ircd_chattr.h ../include/send.h
+  ../include/ircd_string.h ../include/ircd_chattr.h ../include/s_auth.h \
+  ../include/send.h
 m_ping.o: m_ping.c ../config.h ../include/client.h ../include/ircd_defs.h \
   ../include/dbuf.h ../include/msgq.h ../include/ircd_events.h \
   ../config.h ../include/ircd_handler.h ../include/res.h \
@@ -936,7 +927,7 @@ m_pong.o: m_pong.c ../config.h ../include/client.h ../include/ircd_defs.h \
   ../include/struct.h ../include/ircd_log.h ../include/ircd_reply.h \
   ../include/ircd_string.h ../include/ircd_chattr.h ../include/msg.h \
   ../include/numeric.h ../include/numnicks.h ../include/opercmds.h \
-  ../include/s_user.h ../include/send.h
+  ../include/s_auth.h ../include/s_user.h ../include/send.h
 m_privmsg.o: m_privmsg.c ../config.h ../include/client.h \
   ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
   ../include/ircd_events.h ../config.h ../include/ircd_handler.h \
@@ -1116,8 +1107,8 @@ m_user.o: m_user.c ../config.h ../include/handlers.h ../include/client.h \
   ../include/res.h ../include/capab.h ../include/ircd.h \
   ../include/struct.h ../include/ircd_chattr.h ../include/ircd_log.h \
   ../include/ircd_reply.h ../include/ircd_string.h ../include/numeric.h \
-  ../include/numnicks.h ../include/s_debug.h ../include/s_misc.h \
-  ../include/s_user.h ../include/send.h
+  ../include/numnicks.h ../include/s_auth.h ../include/s_debug.h \
+  ../include/s_misc.h ../include/s_user.h ../include/send.h
 m_userhost.o: m_userhost.c ../config.h ../include/client.h \
   ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
   ../include/ircd_events.h ../config.h ../include/ircd_handler.h \
@@ -1272,18 +1263,19 @@ random.o: random.c ../config.h ../include/random.h ../include/client.h \
   ../include/res.h ../include/capab.h ../include/ircd_log.h \
   ../include/ircd_md5.h ../include/ircd_reply.h ../include/send.h
 s_auth.o: s_auth.c ../config.h ../include/s_auth.h \
-  ../include/ircd_events.h ../config.h ../include/client.h \
-  ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
-  ../include/ircd_handler.h ../include/res.h ../include/capab.h \
-  ../include/IPcheck.h ../include/ircd.h ../include/struct.h \
-  ../include/ircd_alloc.h ../include/ircd_chattr.h \
-  ../include/ircd_events.h ../include/ircd_features.h \
-  ../include/ircd_log.h ../include/ircd_osdep.h \
+  ../include/ircd_events.h ../config.h ../include/class.h \
+  ../include/client.h ../include/ircd_defs.h ../include/dbuf.h \
+  ../include/msgq.h ../include/ircd_handler.h ../include/res.h \
+  ../include/capab.h ../include/client.h ../include/IPcheck.h \
+  ../include/ircd.h ../include/struct.h ../include/ircd_alloc.h \
+  ../include/ircd_chattr.h ../include/ircd_events.h \
+  ../include/ircd_features.h ../include/ircd_log.h \
+  ../include/ircd_osdep.h ../include/ircd_reply.h \
   ../include/ircd_snprintf.h ../include/ircd_string.h ../include/list.h \
   ../include/numeric.h ../include/querycmds.h ../include/ircd_features.h \
-  ../include/res.h ../include/s_bsd.h ../include/s_debug.h \
-  ../include/s_misc.h ../include/send.h ../include/struct.h \
-  ../include/sys.h
+  ../include/random.h ../include/res.h ../include/s_bsd.h \
+  ../include/s_conf.h ../include/s_debug.h ../include/s_misc.h \
+  ../include/s_user.h ../include/send.h
 s_bsd.o: s_bsd.c ../config.h ../include/s_bsd.h ../include/client.h \
   ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
   ../include/ircd_events.h ../config.h ../include/ircd_handler.h \
@@ -1308,12 +1300,12 @@ s_conf.o: s_conf.c ../config.h ../include/s_conf.h ../include/client.h \
   ../include/class.h ../include/client.h ../include/crule.h \
   ../include/ircd_features.h ../include/fileio.h ../include/gline.h \
   ../include/hash.h ../include/ircd.h ../include/struct.h \
-  ../include/ircd_alloc.h ../include/ircd_auth.h ../include/ircd_chattr.h \
-  ../include/ircd_log.h ../include/ircd_reply.h \
-  ../include/ircd_snprintf.h ../include/ircd_string.h ../include/list.h \
-  ../include/listener.h ../include/match.h ../include/motd.h \
-  ../include/numeric.h ../include/numnicks.h ../include/opercmds.h \
-  ../include/parse.h ../include/res.h ../include/s_bsd.h \
+  ../include/ircd_alloc.h ../include/ircd_chattr.h ../include/ircd_log.h \
+  ../include/ircd_reply.h ../include/ircd_snprintf.h \
+  ../include/ircd_string.h ../include/list.h ../include/listener.h \
+  ../include/match.h ../include/motd.h ../include/numeric.h \
+  ../include/numnicks.h ../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 ../include/send.h \
   ../include/struct.h ../include/sys.h
 s_debug.o: s_debug.c ../config.h ../include/s_debug.h ../config.h \
@@ -1336,13 +1328,13 @@ s_misc.o: s_misc.c ../config.h ../include/s_misc.h ../include/IPcheck.h \
   ../config.h ../include/client.h ../include/dbuf.h ../include/msgq.h \
   ../include/ircd_events.h ../include/ircd_handler.h ../include/capab.h \
   ../include/hash.h ../include/ircd.h ../include/struct.h \
-  ../include/ircd_alloc.h ../include/ircd_auth.h \
-  ../include/ircd_features.h ../include/ircd_log.h \
-  ../include/ircd_reply.h ../include/ircd_snprintf.h \
-  ../include/ircd_string.h ../include/ircd_chattr.h ../include/list.h \
-  ../include/match.h ../include/msg.h ../include/numeric.h \
-  ../include/numnicks.h ../include/parse.h ../include/querycmds.h \
-  ../include/ircd_features.h ../include/res.h ../include/s_bsd.h \
+  ../include/ircd_alloc.h ../include/ircd_features.h \
+  ../include/ircd_log.h ../include/ircd_reply.h \
+  ../include/ircd_snprintf.h ../include/ircd_string.h \
+  ../include/ircd_chattr.h ../include/list.h ../include/match.h \
+  ../include/msg.h ../include/numeric.h ../include/numnicks.h \
+  ../include/parse.h ../include/querycmds.h ../include/ircd_features.h \
+  ../include/res.h ../include/s_auth.h ../include/s_bsd.h \
   ../include/s_conf.h ../include/client.h ../include/s_debug.h \
   ../include/s_stats.h ../include/s_user.h ../include/send.h \
   ../include/struct.h ../include/sys.h ../include/uping.h \
@@ -1380,28 +1372,28 @@ s_stats.o: s_stats.c ../config.h ../include/class.h ../include/client.h \
   ../include/listener.h ../include/list.h ../include/match.h \
   ../include/motd.h ../include/msg.h ../include/msgq.h \
   ../include/numeric.h ../include/numnicks.h ../include/querycmds.h \
-  ../include/ircd_features.h ../include/res.h ../include/s_bsd.h \
-  ../include/s_conf.h ../include/s_debug.h ../include/s_misc.h \
-  ../include/s_serv.h ../include/s_stats.h ../include/s_user.h \
-  ../include/send.h ../include/struct.h ../include/userload.h
+  ../include/ircd_features.h ../include/res.h ../include/s_auth.h \
+  ../include/s_bsd.h ../include/s_conf.h ../include/s_debug.h \
+  ../include/s_misc.h ../include/s_serv.h ../include/s_stats.h \
+  ../include/s_user.h ../include/send.h ../include/struct.h \
+  ../include/userload.h
 s_user.o: s_user.c ../config.h ../include/s_user.h ../include/IPcheck.h \
   ../include/channel.h ../include/ircd_defs.h ../include/res.h \
   ../config.h ../include/class.h ../include/client.h ../include/dbuf.h \
   ../include/msgq.h ../include/ircd_events.h ../include/ircd_handler.h \
   ../include/capab.h ../include/client.h ../include/hash.h \
   ../include/ircd.h ../include/struct.h ../include/ircd_alloc.h \
-  ../include/ircd_auth.h ../include/ircd_chattr.h \
-  ../include/ircd_features.h ../include/ircd_log.h \
-  ../include/ircd_reply.h ../include/ircd_snprintf.h \
-  ../include/ircd_string.h ../include/list.h ../include/match.h \
-  ../include/motd.h ../include/msg.h ../include/msgq.h \
+  ../include/ircd_chattr.h ../include/ircd_features.h \
+  ../include/ircd_log.h ../include/ircd_reply.h \
+  ../include/ircd_snprintf.h ../include/ircd_string.h ../include/list.h \
+  ../include/match.h ../include/motd.h ../include/msg.h ../include/msgq.h \
   ../include/numeric.h ../include/numnicks.h ../include/parse.h \
   ../include/querycmds.h ../include/ircd_features.h ../include/random.h \
-  ../include/s_bsd.h ../include/s_conf.h ../include/s_debug.h \
-  ../include/s_misc.h ../include/s_serv.h ../include/send.h \
-  ../include/struct.h ../include/supported.h ../include/channel.h \
-  ../include/sys.h ../include/userload.h ../include/version.h \
-  ../include/whowas.h ../include/handlers.h
+  ../include/s_auth.h ../include/s_bsd.h ../include/s_conf.h \
+  ../include/s_debug.h ../include/s_misc.h ../include/s_serv.h \
+  ../include/send.h ../include/struct.h ../include/supported.h \
+  ../include/channel.h ../include/sys.h ../include/userload.h \
+  ../include/version.h ../include/whowas.h ../include/handlers.h
 send.o: send.c ../config.h ../include/send.h ../include/channel.h \
   ../include/ircd_defs.h ../include/res.h ../config.h ../include/class.h \
   ../include/client.h ../include/dbuf.h ../include/msgq.h \
@@ -1462,18 +1454,25 @@ y.tab.o: y.tab.c ../config.h ../include/s_conf.h ../include/client.h \
   ../include/client.h ../include/crule.h ../include/ircd_features.h \
   ../include/fileio.h ../include/gline.h ../include/hash.h \
   ../include/ircd.h ../include/struct.h ../include/ircd_alloc.h \
-  ../include/ircd_auth.h ../include/ircd_chattr.h ../include/ircd_log.h \
-  ../include/ircd_reply.h ../include/ircd_snprintf.h \
-  ../include/ircd_string.h ../include/list.h ../include/listener.h \
-  ../include/match.h ../include/motd.h ../include/numeric.h \
-  ../include/numnicks.h ../include/opercmds.h ../include/parse.h \
-  ../include/res.h ../include/s_bsd.h ../include/s_debug.h \
-  ../include/s_misc.h ../include/send.h ../include/struct.h \
-  ../include/sys.h
+  ../include/ircd_chattr.h ../include/ircd_log.h ../include/ircd_reply.h \
+  ../include/ircd_snprintf.h ../include/ircd_string.h ../include/list.h \
+  ../include/listener.h ../include/match.h ../include/motd.h \
+  ../include/numeric.h ../include/numnicks.h ../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 \
+  ../include/send.h ../include/struct.h ../include/sys.h
+engine_devpoll.o: engine_devpoll.c ../config.h ../include/ircd_events.h \
+  ../config.h ../include/ircd.h ../include/struct.h \
+  ../include/ircd_defs.h ../include/ircd_alloc.h \
+  ../include/ircd_features.h ../include/ircd_log.h ../include/s_debug.h
 engine_poll.o: engine_poll.c ../config.h ../include/ircd_events.h \
   ../config.h ../include/ircd.h ../include/struct.h \
   ../include/ircd_defs.h ../include/ircd_alloc.h ../include/ircd_log.h \
   ../include/s_debug.h
+engine_kqueue.o: engine_kqueue.c ../config.h ../include/ircd_events.h \
+  ../config.h ../include/ircd.h ../include/struct.h \
+  ../include/ircd_defs.h ../include/ircd_alloc.h \
+  ../include/ircd_features.h ../include/ircd_log.h ../include/s_debug.h
 engine_select.o: engine_select.c ../config.h ../include/ircd_events.h \
   ../config.h ../include/ircd.h ../include/struct.h \
   ../include/ircd_defs.h ../include/ircd_log.h ../include/s_debug.h
diff --git a/ircd/ircd_auth.c b/ircd/ircd_auth.c
deleted file mode 100644 (file)
index c38bf5c..0000000
+++ /dev/null
@@ -1,835 +0,0 @@
-/*
- * IRC - Internet Relay Chat, ircd/ircd_auth.c
- * Copyright 2004 Michael Poole <mdpoole@troilus.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-/** @file
- * @brief IAuth client implementation for an IRC server.
- * @version $Id$
- */
-
-#include "config.h"
-#include "client.h"
-#include "ircd_alloc.h"
-#include "ircd_auth.h"
-#include "ircd_events.h"
-#include "ircd_features.h"
-#include "ircd_log.h"
-#include "ircd_osdep.h"
-#include "ircd_snprintf.h"
-#include "ircd_string.h"
-#include "ircd.h"
-#include "msg.h"
-#include "msgq.h"
-#include "res.h"
-#include "s_bsd.h"
-#include "s_debug.h"
-#include "s_misc.h"
-#include "s_user.h"
-#include "send.h"
-
-/* #include <assert.h> -- Now using assert in ircd_log.h */
-#include <errno.h>
-#include <netdb.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#endif
-
-/** Describes state of a single pending IAuth request. */
-struct IAuthRequest {
-  struct IAuthRequest *iar_prev;        /**< previous request struct */
-  struct IAuthRequest *iar_next;        /**< next request struct */
-  struct Client *iar_client;            /**< client being authenticated */
-  char iar_timed;                       /**< if non-zero, using parent i_request_timer */
-};
-
-/** Enumeration of IAuth connection flags. */
-enum IAuthFlag
-{
-  IAUTH_BLOCKED,                        /**< socket buffer full */
-  IAUTH_CONNECTED,                      /**< server greeting/handshake done */
-  IAUTH_ABORT,                          /**< abort connection asap */
-  IAUTH_ICLASS,                         /**< tell iauth about all local users */
-  IAUTH_CLOSING,                        /**< candidate to be disposed */
-  IAUTH_LAST_FLAG                       /**< total number of flags */
-};
-/** Declare a bitset structure indexed by IAuthFlag. */
-DECLARE_FLAGSET(IAuthFlags, IAUTH_LAST_FLAG);
-
-/** Describes state of an IAuth connection. */
-struct IAuth {
-  struct IAuthRequest i_list_head;      /**< doubly linked list of requests */
-  struct MsgQ i_sendQ;                  /**< messages queued to send */
-  struct Socket i_socket;               /**< connection to server */
-  struct Timer i_reconn_timer;          /**< when to reconnect the connection */
-  struct Timer i_request_timer;         /**< when the current request times out */
-  struct IAuthFlags i_flags;            /**< connection state/status/flags */
-  unsigned int i_recvM;                 /**< messages received */
-  unsigned int i_sendM;                 /**< messages sent */
-  unsigned int i_recvK;                 /**< kilobytes received */
-  unsigned int i_sendK;                 /**< kilobytes sent */
-  unsigned short i_recvB;               /**< bytes received modulo 1024 */
-  unsigned short i_sendB;               /**< bytes sent modulo 1024 */
-  time_t i_reconnect;                   /**< seconds to wait before reconnecting */
-  time_t i_timeout;                     /**< seconds to wait for a request */
-  unsigned int i_count;                 /**< characters used in i_buffer */
-  char i_buffer[BUFSIZE+1];             /**< partial unprocessed line from server */
-  char i_passwd[PASSWDLEN+1];           /**< password for connection */
-  char i_host[HOSTLEN+1];               /**< iauth server hostname */
-  struct irc_sockaddr i_addr;           /**< iauth server ip address and port */
-  struct IAuth *i_next;                 /**< next connection in list */
-};
-
-/** Return flags element of \a iauth. */
-#define i_flags(iauth) ((iauth)->i_flags)
-/** Return whether flag \a flag is set on \a iauth. */
-#define IAuthGet(iauth, flag) FlagHas(&i_flags(iauth), flag)
-/** Set flag \a flag on \a iauth. */
-#define IAuthSet(iauth, flag) FlagSet(&i_flags(iauth), flag)
-/** Clear flag \a flag from \a iauth. */
-#define IAuthClr(iauth, flag) FlagClr(&i_flags(iauth), flag)
-/** Get blocked state for \a iauth. */
-#define i_GetBlocked(iauth) IAuthGet(iauth, IAUTH_BLOCKED)
-/** Set blocked state for \a iauth. */
-#define i_SetBlocked(iauth) IAuthSet(iauth, IAUTH_BLOCKED)
-/** Clear blocked state for \a iauth. */
-#define i_ClrBlocked(iauth) IAuthClr(iauth, IAUTH_BLOCKED)
-/** Get connected flag for \a iauth. */
-#define i_GetConnected(iauth) IAuthGet(iauth, IAUTH_CONNECTED)
-/** Set connected flag for \a iauth. */
-#define i_SetConnected(iauth) IAuthSet(iauth, IAUTH_CONNECTED)
-/** Clear connected flag for \a iauth. */
-#define i_ClrConnected(iauth) IAuthClr(iauth, IAUTH_CONNECTED)
-/** Get abort flag for \a iauth. */
-#define i_GetAbort(iauth) IAuthGet(iauth, IAUTH_ABORT)
-/** Set abort flag for \a iauth. */
-#define i_SetAbort(iauth) IAuthSet(iauth, IAUTH_ABORT)
-/** Clear abort flag for \a iauth. */
-#define i_ClrAbort(iauth) IAuthClr(iauth, IAUTH_ABORT)
-/** Get IClass flag for \a iauth. */
-#define i_GetIClass(iauth) IAuthGet(iauth, IAUTH_ICLASS)
-/** Set IClass flag for \a iauth. */
-#define i_SetIClass(iauth) IAuthSet(iauth, IAUTH_ICLASS)
-/** Clear IClass flag for \a iauth. */
-#define i_ClrIClass(iauth) IAuthClr(iauth, IAUTH_ICLASS)
-/** Get closing flag for \a iauth. */
-#define i_GetClosing(iauth) IAuthGet(iauth, IAUTH_CLOSING)
-/** Set closing flag for \a iauth. */
-#define i_SetClosing(iauth) IAuthSet(iauth, IAUTH_CLOSING)
-/** Clear closing flag for \a iauth. */
-#define i_ClrClosing(iauth) IAuthClr(iauth, IAUTH_CLOSING)
-
-/** Return head of request linked list for \a iauth. */
-#define i_list_head(iauth) ((iauth)->i_list_head)
-/** Return socket event generator for \a iauth. */
-#define i_socket(iauth) ((iauth)->i_socket)
-/** Return reconnect timer for \a iauth. */
-#define i_reconn_timer(iauth) ((iauth)->i_reconn_timer)
-/** Return request timeout timer for \a iauth. */
-#define i_request_timer(iauth) ((iauth)->i_request_timer)
-/** Return DNS query for \a iauth. */
-#define i_query(iauth) ((iauth)->i_query)
-/** Return received bytes (modulo 1024) for \a iauth. */
-#define i_recvB(iauth) ((iauth)->i_recvB)
-/** Return received kilobytes (modulo 1024) for \a iauth. */
-#define i_recvK(iauth) ((iauth)->i_recvK)
-/** Return received megabytes for \a iauth. */
-#define i_recvM(iauth) ((iauth)->i_recvM)
-/** Return sent bytes (modulo 1024) for \a iauth. */
-#define i_sendB(iauth) ((iauth)->i_sendB)
-/** Return sent kilobytes (modulo 1024) for \a iauth. */
-#define i_sendK(iauth) ((iauth)->i_sendK)
-/** Return sent megabytes for \a iauth. */
-#define i_sendM(iauth) ((iauth)->i_sendM)
-/** Return outbound message queue for \a iauth. */
-#define i_sendQ(iauth) ((iauth)->i_sendQ)
-/** Return reconnection interval for \a iauth. */
-#define i_reconnect(iauth) ((iauth)->i_reconnect)
-/** Return request timeout interval for \a iauth. */
-#define i_timeout(iauth) ((iauth)->i_timeout)
-/** Return length of unprocessed message data for \a iauth. */
-#define i_count(iauth) ((iauth)->i_count)
-/** Return start of unprocessed message data for \a iauth. */
-#define i_buffer(iauth) ((iauth)->i_buffer)
-/** Return password we send for \a iauth. */
-#define i_passwd(iauth) ((iauth)->i_passwd)
-/** Return server hostname for \a iauth. */
-#define i_host(iauth) ((iauth)->i_host)
-/** Return address of IAuth server for \a iauth. */
-#define i_addr(iauth) ((iauth)->i_addr)
-/** Return server port for \a iauth. */
-#define i_port(iauth) ((iauth)->i_addr.port)
-/** Return next IAuth connection after \a iauth. */
-#define i_next(iauth) ((iauth)->i_next)
-
-/** Command table entry. */
-struct IAuthCmd {
-  const char *iac_name; /**< Name of command. */
-  void (*iac_func)(struct IAuth *iauth, int, char *[]); /**< Handler function. */
-};
-
-/** Active %IAuth connection(s). */
-struct IAuth *iauth_active;
-
-static void iauth_write(struct IAuth *iauth);
-static void iauth_reconnect(struct IAuth *iauth);
-static void iauth_disconnect(struct IAuth *iauth);
-static void iauth_sock_callback(struct Event *ev);
-static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar);
-static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar);
-static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[]);
-static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[]);
-
-/** Table of responses we might get from the IAuth server. */
-static const struct IAuthCmd iauth_cmdtab[] = {
-  { "DoneAuth", iauth_cmd_doneauth },
-  { "BadAuth", iauth_cmd_badauth },
-  { NULL, NULL }
-};
-
-/** Start (or update) a connection to an %IAuth server.
- * If a connection already exists for the specified server name and
- * port, update it with the other parameters; otherwise allocate a new
- * IAuth record.
- * @param[in] host %IAuth server hostname.
- * @param[in] port %IAuth server port.
- * @param[in] passwd Password to send.
- * @param[in] reconnect Reconnect interval.
- * @param[in] timeout Request timeout interval.
- * @return IAuth structure for that connection.
- */
-struct IAuth *iauth_connect(char *host, unsigned short port, char *passwd, time_t reconnect, time_t timeout)
-{
-  struct IAuth *iauth;
-
-  for (iauth = iauth_active; iauth; iauth = i_next(iauth)) {
-    if (!ircd_strncmp(i_host(iauth), host, HOSTLEN)
-        && (i_port(iauth) == port)) {
-      i_ClrClosing(iauth);
-      i_reconnect(iauth) = reconnect;
-      if (t_active(&i_reconn_timer(iauth)) && (t_expire(&i_reconn_timer(iauth)) > CurrentTime + i_reconnect(iauth)))
-        timer_chg(&i_reconn_timer(iauth), TT_RELATIVE, i_reconnect(iauth));
-      break;
-    }
-  }
-  if (NULL == iauth) {
-    if (iauth_active && !i_GetClosing(iauth_active)) {
-      log_write(LS_CONFIG, L_WARNING, 0, "Creating extra active IAuth connection to %s:%d.", host, port);
-    }
-    iauth = MyCalloc(1, sizeof(*iauth));
-    i_list_head(iauth).iar_prev = &i_list_head(iauth);
-    i_list_head(iauth).iar_next = &i_list_head(iauth);
-    msgq_init(&i_sendQ(iauth));
-    ircd_strncpy(i_host(iauth), host, HOSTLEN);
-    memset(&i_addr(iauth), 0, sizeof(i_addr(iauth)));
-    i_port(iauth) = port;
-    iauth_active = iauth;
-    timer_init(&i_reconn_timer(iauth));
-    i_reconnect(iauth) = reconnect;
-    iauth_reconnect(iauth);
-  }
-  if (passwd)
-    ircd_strncpy(i_passwd(iauth), passwd, PASSWDLEN);
-  else
-    i_passwd(iauth)[0] = '\0';
-  i_timeout(iauth) = timeout;
-  i_SetIClass(iauth);
-  return iauth;
-}
-
-/** Mark all %IAuth connections as closing. */
-void iauth_mark_closing(void)
-{
-  struct IAuth *iauth;
-  for (iauth = iauth_active; iauth; iauth = i_next(iauth))
-    i_SetClosing(iauth);
-}
-
-/** Close a particular %IAuth connection.
- * @param[in] iauth %Connection to close.
- */
-void iauth_close(struct IAuth *iauth)
-{
-  /* Figure out what to do with the closing connection's requests. */
-  if (i_list_head(iauth).iar_next != &i_list_head(iauth)) {
-    struct IAuthRequest *iar;
-    if (iauth_active || i_next(iauth)) {
-      /* If iauth_active != NULL, send requests to it; otherwise if
-       * i_next(iauth) != NULL, we can hope it or some later
-       * connection will be active.
-       */
-      struct IAuth *target = iauth_active ? iauth_active : i_next(iauth);
-
-      /* Append iauth->i_list_head to end of target->i_list_head. */
-      iar = i_list_head(iauth).iar_next;
-      iar->iar_prev = i_list_head(target).iar_prev;
-      i_list_head(target).iar_prev->iar_next = iar;
-      iar = i_list_head(iauth).iar_prev;
-      iar->iar_next = &i_list_head(target);
-      i_list_head(target).iar_prev = iar;
-
-      /* If the target is not closing, send the requests. */
-      for (iar = i_list_head(iauth).iar_next;
-           iar != &i_list_head(target);
-           iar = iar->iar_next) {
-        if (!i_GetClosing(target))
-          iauth_send_request(target, iar);
-      }
-    } else {
-      /* No active connections - approve the requests and drop them. */
-      while ((iar = i_list_head(iauth).iar_next) != &i_list_head(iauth)) {
-        struct Client *client = iar->iar_client;
-        iauth_dispose_request(iauth, iar);
-        register_user(client, client, cli_name(client), cli_username(client));
-      }
-    }
-  }
-  /* Make sure the connection closes with an empty request list. */
-  i_list_head(iauth).iar_prev = &i_list_head(iauth);
-  i_list_head(iauth).iar_next = &i_list_head(iauth);
-  /* Cancel the timer, if it is active. */
-  if (t_active(&i_reconn_timer(iauth)))
-    timer_del(&i_reconn_timer(iauth));
-  if (t_active(&i_request_timer(iauth)))
-    timer_del(&i_request_timer(iauth));
-  /* Disconnect from the server. */
-  if (i_GetConnected(iauth))
-    iauth_disconnect(iauth);
-  /* Free memory. */
-  MyFree(iauth);
-}
-
-/** Close all %IAuth connections marked as closing. */
-void iauth_close_unused(void)
-{
-  struct IAuth *prev, *iauth, *next;
-
-  for (prev = NULL, iauth = iauth_active; iauth; iauth = next) {
-    next = i_next(iauth);
-    if (i_GetClosing(iauth)) {
-      /* Update iauth_active linked list. */
-      if (prev)
-        i_next(prev) = next;
-      else
-        iauth_active = next;
-      /* Close and destroy the connection. */
-      iauth_close(iauth);
-    } else {
-      prev = iauth;
-    }
-  }
-}
-
-/** Send a line to an %IAuth server.
- * @param[in] iauth %Connection to send on.
- * @param[in] format Format string for message.
- */
-static void iauth_send(struct IAuth *iauth, const char *format, ...)
-{
-  va_list vl;
-  struct MsgBuf *mb;
-
-  va_start(vl, format);
-  mb = msgq_vmake(0, format, vl);
-  va_end(vl);
-  msgq_add(&i_sendQ(iauth), mb, 0);
-  msgq_clean(mb);
-}
-
-/** Report a protocol violation from the %IAuth server.
- * @param[in] iauth %Connection that experienced the violation.
- * @param[in] format Format string for message to operators.
- */
-static void iauth_protocol_violation(struct IAuth *iauth, const char *format, ...)
-{
-  struct VarData vd;
-  assert(iauth != 0);
-  assert(format != 0);
-  vd.vd_format = format;
-  va_start(vd.vd_args, format);
-  sendto_opmask_butone(NULL, SNO_CONNEXIT, "IAuth protocol violation: %v", &vd);
-  va_end(vd.vd_args);
-}
-
-/** Send on-connect burst to an %IAuth server.
- * @param[in] iauth %Connection that has completed.
- */
-static void iauth_on_connect(struct IAuth *iauth)
-{
-  struct IAuthRequest *iar;
-  if (EmptyString(i_passwd(iauth)))
-    iauth_send(iauth, "Server %s", cli_name(&me));
-  else
-    iauth_send(iauth, "Server %s %s", cli_name(&me), i_passwd(iauth));
-  if (i_GetIClass(iauth)) {
-    /* TODO: report local users to iauth */
-    iauth_send(iauth, "EndUsers");
-  }
-  i_SetConnected(iauth);
-  for (iar = i_list_head(iauth).iar_next;
-       iar != &i_list_head(iauth);
-       iar = iar->iar_next)
-    iauth_send_request(iauth, iar);
-  iauth_write(iauth);
-}
-
-/** Complete disconnection of an %IAuth connection.
- * @param[in] iauth %Connection to fully close.
- */
-static void iauth_disconnect(struct IAuth *iauth)
-{
-  close(s_fd(&i_socket(iauth)));
-  socket_del(&i_socket(iauth));
-  i_ClrConnected(iauth);
-}
-
-/** DNS completion callback for an %IAuth connection.
- * @param[in] vptr Pointer to the IAuth struct.
- * @param[in] he DNS reply parameters.
- */
-static void iauth_dns_callback(void *vptr, const struct irc_in_addr *addr, const char *h_name)
-{
-  struct IAuth *iauth = vptr;
-  if (!addr) {
-    log_write(LS_IAUTH, L_NOTICE, 0, "IAuth connection to %s failed: host lookup failed", i_host(iauth));
-  } else {
-    memcpy(&i_addr(iauth).addr, addr, sizeof(i_addr(iauth).addr));
-    if (!irc_in_addr_valid(&i_addr(iauth).addr)) {
-      log_write(LS_IAUTH, L_NOTICE, 0, "IAuth connection to %s failed: host came back as unresolved", i_host(iauth));
-      return;
-    }
-    iauth_reconnect(iauth);
-  }
-}
-
-/** Timer callback for reconnecting to %IAuth.
- * @param[in] ev Timer event for reconnect.
- */
-static void iauth_reconnect_ev(struct Event *ev)
-{
-  if (ev_type(ev) == ET_EXPIRE)
-    iauth_reconnect(t_data(ev_timer(ev)));
-}
-
-/** Schedule a reconnection for \a iauth.
- * @param[in] iauth %Connection that needs to be reconnected.
- */
-static void iauth_schedule_reconnect(struct IAuth *iauth)
-{
-  struct Timer *timer;
-  timer = &i_reconn_timer(iauth);
-  if (t_onqueue(timer))
-    timer_chg(timer, TT_RELATIVE, i_reconnect(iauth));
-  else
-    timer_add(&i_reconn_timer(iauth), iauth_reconnect_ev,
-              iauth, TT_RELATIVE, i_reconnect(iauth));
-}
-
-/** Initiate a (re-)connection to \a iauth.
- * @param[in] iauth %Connection that should be initiated.
- */
-static void iauth_reconnect(struct IAuth *iauth)
-{
-  struct irc_sockaddr *local;
-  IOResult result;
-  int fd;
-
-  if (i_GetConnected(iauth)) {
-    iauth_disconnect(iauth);
-    iauth_schedule_reconnect(iauth);
-    return;
-  }
-  log_write(LS_IAUTH, L_DEBUG, 0, "IAuth attempt connection to %s port %p.", i_host(iauth), i_port(iauth));
-  if (!irc_in_addr_valid(&i_addr(iauth).addr)
-      && !ircd_aton(&i_addr(iauth).addr, i_host(iauth))) {
-    gethost_byname(i_host(iauth), iauth_dns_callback, iauth);
-    return;
-  }
-  local = irc_in_addr_is_ipv4(&i_addr(iauth).addr) ? &VirtualHost_v4 : &VirtualHost_v6;
-  fd = os_socket(local, SOCK_STREAM, "IAuth");
-  if (fd < 0) {
-    iauth_schedule_reconnect(iauth);
-    return;
-  }
-  if (!os_set_sockbufs(fd, SERVER_TCP_WINDOW, SERVER_TCP_WINDOW)) {
-    log_write(LS_IAUTH, L_WARNING, 0, "IAuth reconnect unable to set socket buffers: %s", strerror(errno));
-    goto failure;
-  }
-  s_fd(&i_socket(iauth)) = fd;
-  result = os_connect_nonb(fd, &i_addr(iauth));
-  if (result == IO_FAILURE) {
-    log_write(LS_IAUTH, L_NOTICE, 0, "IAuth reconnect unable to initiate connection: %s", strerror(errno));
-    goto failure;
-  }
-  if (!socket_add(&i_socket(iauth), iauth_sock_callback, iauth,
-                  (result == IO_SUCCESS) ? SS_CONNECTED : SS_CONNECTING,
-                  SOCK_EVENT_READABLE | SOCK_EVENT_WRITABLE, fd)) {
-    log_write(LS_IAUTH, L_WARNING, 0, "IAuth reconnect unable to add socket: %s", strerror(errno));
-    goto failure;
-  }
-  return;
-failure:
-  close(fd);
-  i_ClrConnected(iauth);
-  iauth_schedule_reconnect(iauth);
-  return;
-}
-
-/** Read input from \a iauth.
- * Reads up to SERVER_TCP_WINDOW bytes per pass.
- * @param[in] iauth Readable connection.
- */
-static void iauth_read(struct IAuth *iauth)
-{
-  char *src, *endp, *old_buffer, *argv[MAXPARA + 1];
-  unsigned int length, argc, ii;
-  char readbuf[SERVER_TCP_WINDOW];
-
-  length = 0;
-  if (IO_FAILURE == os_recv_nonb(s_fd(&i_socket(iauth)), readbuf, sizeof(readbuf), &length)
-      || length == 0) {
-      iauth_reconnect(iauth);
-      return;
-  }
-  i_recvB(iauth) += length;
-  if (i_recvB(iauth) > 1023) {
-    i_recvK(iauth) += i_recvB(iauth) >> 10;
-    i_recvB(iauth) &= 1023;
-  }
-  old_buffer = i_buffer(iauth);
-  endp = old_buffer + i_count(iauth);
-  for (src = readbuf; length > 0; --length) {
-    *endp = *src++;
-    if (IsEol(*endp)) {
-      /* Skip blank lines. */
-      if (endp == old_buffer)
-        continue;
-      /* NUL-terminate line and split parameters. */
-      *endp = '\0';
-      for (argc = 0, endp = old_buffer; *endp && (argc < MAXPARA); ) {
-        while (*endp == ' ')
-          *endp++ = '\0';
-        if (*endp == '\0')
-          break;
-        if (*endp == ':')
-        {
-          argv[argc++] = endp + 1;
-          break;
-        }
-        argv[argc++] = endp;
-        for (; *endp && *endp != ' '; ++endp) ;
-      }
-      argv[argc] = NULL;
-      /* Count line and reset endp to start of buffer. */
-      i_recvM(iauth)++;
-      endp = old_buffer;
-      /* Look up command and try to dispatch. */
-      if (argc > 0) {
-        for (ii = 0; iauth_cmdtab[ii].iac_name; ++ii) {
-          if (!ircd_strcmp(iauth_cmdtab[ii].iac_name, argv[0])) {
-            iauth_cmdtab[ii].iac_func(iauth, argc, argv);
-            if (i_GetAbort(iauth))
-              iauth_disconnect(iauth);
-            break;
-          }
-        }
-      }
-    }
-    else if (endp < old_buffer + BUFSIZE)
-      endp++;
-  }
-  i_count(iauth) = endp - old_buffer;
-}
-
-/** Send queued output to \a iauth.
- * @param[in] iauth Writable connection with queued data.
- */
-static void iauth_write(struct IAuth *iauth)
-{
-  unsigned int bytes_tried, bytes_sent;
-  IOResult iores;
-
-  if (i_GetBlocked(iauth))
-    return;
-  while (MsgQLength(&i_sendQ(iauth)) > 0) {
-    iores = os_sendv_nonb(s_fd(&i_socket(iauth)), &i_sendQ(iauth), &bytes_tried, &bytes_sent);
-    switch (iores) {
-    case IO_SUCCESS:
-      msgq_delete(&i_sendQ(iauth), bytes_sent);
-      i_sendB(iauth) += bytes_sent;
-      if (i_sendB(iauth) > 1023) {
-        i_sendK(iauth) += i_sendB(iauth) >> 10;
-        i_sendB(iauth) &= 1023;
-      }
-      if (bytes_tried == bytes_sent)
-        break;
-      /* If bytes_sent < bytes_tried, fall through to IO_BLOCKED. */
-    case IO_BLOCKED:
-      i_SetBlocked(iauth);
-      socket_events(&i_socket(iauth), SOCK_ACTION_ADD | SOCK_EVENT_WRITABLE);
-      return;
-    case IO_FAILURE:
-      iauth_disconnect(iauth);
-      return;
-    }
-  }
-  /* We were able to flush all events, so remove notification. */
-  socket_events(&i_socket(iauth), SOCK_ACTION_DEL | SOCK_EVENT_WRITABLE);
-}
-
-/** Handle socket activity for an %IAuth connection.
- * @param[in] ev &Socket event; the IAuth connection is the user data pointer for the socket.
- */
-static void iauth_sock_callback(struct Event *ev)
-{
-  struct IAuth *iauth;
-
-  assert(0 != ev_socket(ev));
-  iauth = (struct IAuth*) s_data(ev_socket(ev));
-  assert(0 != iauth);
-
-  switch (ev_type(ev)) {
-  case ET_CONNECT:
-    socket_state(ev_socket(ev), SS_CONNECTED);
-    iauth_on_connect(iauth);
-    break;
-  case ET_DESTROY:
-    if (!i_GetClosing(iauth))
-      iauth_schedule_reconnect(iauth);
-    break;
-  case ET_READ:
-    iauth_read(iauth);
-    break;
-  case ET_WRITE:
-    i_ClrBlocked(iauth);
-    iauth_write(iauth);
-    break;
-  case ET_ERROR:
-    log_write(LS_IAUTH, L_ERROR, 0, "IAuth socket error: %s", strerror(ev_data(ev)));
-    /* and fall through to the ET_EOF case */
-  case ET_EOF:
-    iauth_disconnect(iauth);
-    iauth_schedule_reconnect(iauth);
-    break;
-  default:
-    assert(0 && "Unrecognized event type");
-    break;
-  }
-}
-
-/* Functions related to IAuthRequest structs */
-
-/** Handle timeout while waiting for a response.
- * @param[in] ev Timer event that expired.
- */
-static void iauth_request_ev(struct Event *ev)
-{
-  /* TODO: this could probably be more intelligent */
-  if (ev_type(ev) == ET_EXPIRE) {
-    log_write(LS_IAUTH, L_NOTICE, 0, "IAuth request timed out; reconnecting");
-    iauth_reconnect(t_data(ev_timer(ev)));
-  }
-}
-
-/** Send a authorization request to an %IAuth server.
- * @param[in] iauth %Connection to send request on.
- * @param[in] iar Request to send.
- */
-static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar)
-{
-  struct Client *client;
-
-  /* If iauth is not connected, we must defer the request. */
-  if (!i_GetConnected(iauth)) {
-    Debug((DEBUG_SEND, "IAuth deferring request for %s because we are not connected.", cli_name(iar->iar_client)));
-    return;
-  }
-
-  /* If no timed request, set up expiration timer. */
-  if (!t_active(&i_request_timer(iauth))) {
-    struct Timer *timer = timer_init(&i_request_timer(iauth));
-    timer_add(timer, iauth_request_ev, iauth, TT_RELATIVE, i_timeout(iauth));
-    iar->iar_timed = 1;
-  } else
-    iar->iar_timed = 0;
-
-  /* Send the FullAuth request. */
-  client = iar->iar_client;
-  assert(iar->iar_client != NULL);
-  iauth_send(iauth, "FullAuth %x %s %s %s %s %s :%s",
-             client, cli_name(client), cli_username(client),
-             cli_user(client)->host, cli_sock_ip(client),
-             cli_passwd(client), cli_info(client));
-
-  /* Write to the socket if we can. */
-  iauth_write(iauth);
-}
-
-/** Start independent authorization check for a client.
- * @param[in] iauth %Connection to send request on.
- * @param[in] cptr Client to check.
- * @return Zero, or CPTR_KILLED in case of memory allocation failure.
- */
-int iauth_start_client(struct IAuth *iauth, struct Client *cptr)
-{
-  struct IAuthRequest *iar;
-
-  /* Allocate and initialize IAuthRequest struct. */
-  if (!(iar = MyCalloc(1, sizeof(*iar))))
-    return exit_client(cptr, cptr, &me, "IAuth memory allocation failed");
-  cli_iauth(cptr) = iar;
-  iar->iar_next = &i_list_head(iauth);
-  iar->iar_prev = i_list_head(iauth).iar_prev;
-  iar->iar_client = cptr;
-  iar->iar_prev->iar_next = iar;
-  iar->iar_next->iar_prev = iar;
-
-  /* Send request. */
-  iauth_send_request(iauth, iar);
-
-  return 0;
-}
-
-/** Handle a client that is disconnecting.
- * If there is a pending %IAuth request for the client, close it.
- * @param[in] cptr Client that is disconnecting.
- */
-void iauth_exit_client(struct Client *cptr)
-{
-  if (cli_iauth(cptr)) {
-    iauth_dispose_request(iauth_active, cli_iauth(cptr));
-    cli_iauth(cptr) = NULL;
-  }
-  if (iauth_active && i_GetConnected(iauth_active)) {
-    iauth_send(iauth_active, "ExitUser %x", cptr);
-    iauth_write(iauth_active);
-  }
-}
-
-/** Find pending request with a particular ID.
- * @param[in] iauth %Connection context for the ID.
- * @param[in] id Identifier to look up.
- * @return IAuthRequest with that ID, or NULL.
- */
-static struct IAuthRequest *iauth_find_request(struct IAuth *iauth, char *id)
-{
-  struct IAuthRequest *curr;
-  struct Client *target;
-  target = (struct Client*)strtoul(id, NULL, 16);
-  for (curr = i_list_head(iauth).iar_next;
-       curr != &i_list_head(iauth);
-       curr = curr->iar_next) {
-    assert(curr->iar_client != NULL);
-    if (target == curr->iar_client)
-      return curr;
-  }
-  return NULL;
-}
-
-/** Unlink and free a request.
- * @param[in] iauth Connection that owns the request.
- * @param[in] iar Request to free.
- */
-static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar)
-{
-  assert(iar->iar_client != NULL);
-  if (iar->iar_timed && t_active(&i_request_timer(iauth)))
-    timer_del(&i_request_timer(iauth));
-  cli_iauth(iar->iar_client) = NULL;
-  iar->iar_prev->iar_next = iar->iar_next;
-  iar->iar_next->iar_prev = iar->iar_prev;
-  MyFree(iar);
-}
-
-/** Handle a DoneAuth response from %IAuth.
- * This means the client is authorized, so let them in.
- * @param[in] iauth Connection that sent the message.
- * @param[in] argc Argument count.
- * @param[in] argv Argument list.
- */
-static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[])
-{
-  struct IAuthRequest *iar;
-  struct Client *client;
-  char *id;
-  char *username;
-  char *hostname;
-  char *c_class;
-  char *account;
-
-  if (argc < 5) {
-    iauth_protocol_violation(iauth, "Only %d parameters for DoneAuth (expected >=5)", argc);
-    return;
-  }
-  id = argv[1];
-  username = argv[2];
-  hostname = argv[3];
-  c_class = argv[4];
-  account = (argc > 5) ? argv[5] : 0;
-  iar = iauth_find_request(iauth, id);
-  if (!iar) {
-    iauth_protocol_violation(iauth, "Got unexpected DoneAuth for id %s", id);
-    return;
-  }
-  client = iar->iar_client;
-  ircd_strncpy(cli_username(client), username, USERLEN);
-  ircd_strncpy(cli_user(client)->host, hostname, HOSTLEN);
-  if (account) {
-    ircd_strncpy(cli_user(client)->account, account, ACCOUNTLEN);
-    SetAccount(client);
-  }
-  SetIAuthed(client);
-  iauth_dispose_request(iauth, iar);
-  register_user(client, client, cli_name(client), username);
-}
-
-/** Handle a BadAuth response from %IAuth.
- * This means the client is not authorized, so dump them.
- * @param[in] iauth Connection that sent the message.
- * @param[in] argc Argument count.
- * @param[in] argv Argument list.
- */
-static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[])
-{
-  struct IAuthRequest *iar;
-  struct Client *client;
-  char *id;
-  char *reason;
-
-  if (argc < 3) {
-    iauth_protocol_violation(iauth, "Only %d parameters for BadAuth (expected >=3)", argc);
-    return;
-  }
-  id = argv[1];
-  reason = argv[2];
-  if (EmptyString(reason)) {
-    iauth_protocol_violation(iauth, "Empty BadAuth reason for id %s", id);
-    return;
-  }
-  iar = iauth_find_request(iauth, id);
-  if (!iar) {
-    iauth_protocol_violation(iauth, "Got unexpected BadAuth for id %s", id);
-    return;
-  }
-  client = iar->iar_client;
-  iauth_dispose_request(iauth, iar);
-  exit_client(client, client, &me, reason);
-}
index 0ffad1a201728cc2d4c607ca9966fa22c5e2a46a..fa33160228642ac355535688eada3f35bd4e1094 100644 (file)
@@ -388,6 +388,7 @@ static struct FeatureDesc {
   F_B(HIS_STATS_x, 0, 1, 0),
   F_B(HIS_STATS_y, 0, 1, 0),
   F_B(HIS_STATS_z, 0, 1, 0),
+  F_B(HIS_STATS_IAUTH, 0, 1, 0),
   F_B(HIS_WHOIS_SERVERNAME, 0, 1, 0),
   F_B(HIS_WHOIS_IDLETIME, 0, 1, 0),
   F_B(HIS_WHOIS_LOCALCHAN, 0, 1, 0),
index 0df1f707437dda00f5efa20a389570a9f64f5f68..4ec8a67cbb66cad9405bc664b36b071289ff2107 100644 (file)
@@ -102,6 +102,7 @@ static struct lexer_token {
   TOKEN(USERMODE),
   TOKEN(FAST),
   TOKEN(AUTOCONNECT),
+  TOKEN(PROGRAM),
 #undef TOKEN
   { "administrator", ADMIN },
   { "apass_opmode", TPRIV_APASS_OPMODE },
index 02aeb135ede3d2eaa27d97bf4192beaab88b755f..dda7141d00de3a88c4ad2de955a2389b81f03edf 100644 (file)
@@ -126,7 +126,7 @@ static struct {
   M(NONE),       M(OLDSNO),     M(SERVKILL),   M(OPERKILL),   M(HACK2),
   M(HACK3),      M(UNAUTH),     M(TCPCOMMON),  M(TOOMANY),    M(HACK4),
   M(GLINE),      M(NETWORK),    M(IPMISMATCH), M(THROTTLE),   M(OLDREALOP),
-  M(CONNEXIT),   M(DEBUG),
+  M(CONNEXIT),   M(DEBUG),      M(AUTH),
 #undef M
   { 0, 0 }
 };
index bd86914c884fc14a54c18130a45837dccd59457d..692616b66ab79e73654b439fa0a88a4480eabfe7 100644 (file)
@@ -32,7 +32,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"
@@ -47,6 +46,7 @@
 #include "opercmds.h"
 #include "parse.h"
 #include "res.h"
+#include "s_auth.h"
 #include "s_bsd.h"
 #include "s_conf.h"
 #include "s_debug.h"
@@ -156,6 +156,7 @@ static void parse_error(char *pattern,...) {
 %token TIMEOUT
 %token FAST
 %token AUTOCONNECT
+%token PROGRAM
 /* and now a lot of privileges... */
 %token TPRIV_CHAN_LIMIT TPRIV_MODE_LCHAN TPRIV_DEOP_LCHAN TPRIV_WALK_LCHAN
 %token TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE
@@ -910,21 +911,14 @@ featureitem: QSTRING
 {
   stringlist[0] = $1;
   stringno = 1;
-} '=' stringlist ';';
-
-stringlist: QSTRING
-{
-  stringlist[1] = $1;
-  stringno = 2;
-} posextrastrings
-{
+} '=' stringlist ';' {
   unsigned int ii;
   feature_set(NULL, (const char * const *)stringlist, stringno);
   for (ii = 0; ii < stringno; ++ii)
     MyFree(stringlist[ii]);
 };
-posextrastrings: /* empty */ | extrastrings;
-extrastrings: extrastrings extrastring | extrastring;
+
+stringlist: stringlist extrastring | extrastring;
 extrastring: QSTRING
 {
   if (stringno < MAX_STRINGS)
@@ -1002,45 +996,17 @@ pseudoflags: FAST ';'
   smap->flags |= SMAP_FAST;
 };
 
-iauthblock: IAUTH '{'
+iauthblock: IAUTH '{' iauthitems '}' ';'
 {
-  tconn = 60;
-  tping = 60;
-} iauthitems '}' ';'
-{
-  if (!host)
-    parse_error("Missing host in iauth block");
-  else if (!port)
-    parse_error("Missing port in iauth block");
-  else
-    iauth_connect(host, port, pass, tconn, tping);
-  MyFree(pass);
-  MyFree(host);
-  pass = host = NULL;
-  port = tconn = tping = 0;
+  auth_spawn(stringno, stringlist);
+  while (stringno > 0)
+    MyFree(stringlist[stringno--]);
 };
 
 iauthitems: iauthitem iauthitems | iauthitem;
-iauthitem: iauthpass | iauthhost | iauthport | iauthconnfreq | iauthtimeout;
-iauthpass: PASS '=' QSTRING ';'
-{
-  MyFree(pass);
-  pass = $3;
-};
-iauthhost: HOST '=' QSTRING ';'
+iauthitem: iauthprogram;
+iauthprogram: PROGRAM '='
 {
-  MyFree(host);
-  host = $3;
-};
-iauthport: PORT '=' NUMBER ';'
-{
-  port = $3;
-};
-iauthconnfreq: CONNECTFREQ '=' timespec ';'
-{
-  tconn = $3;
-};
-iauthtimeout: TIMEOUT '=' timespec ';'
-{
-  tping = $3;
-};
+  while (stringno > 0)
+    MyFree(stringlist[stringno--]);
+} stringlist ';';
index dcbe466efb259a656984494edc052a5fa3fc1dc6..cbf98c5eedccacdb65227182337ca82dd63eb06b 100644 (file)
@@ -219,7 +219,6 @@ struct Client* make_client(struct Client *from, int status)
     cli_connect(cptr) = con; /* set the connection and other fields */
     cli_since(cptr) = cli_lasttime(cptr) = cli_firsttime(cptr) = CurrentTime;
     cli_lastnick(cptr) = TStime();
-    cli_unreg(cptr) = CLIREG_INIT;
   } else
     cli_connect(cptr) = cli_connect(from); /* use 'from's connection */
 
index a41593306cd0e7d66b760592955cedf4f5b56faa..2443c42f714d9f0046b470f9b177234e359b4717 100644 (file)
@@ -36,6 +36,7 @@
 #include "msg.h"
 #include "numeric.h"
 #include "send.h"
+#include "s_auth.h"
 #include "s_user.h"
 
 #include <stdlib.h>
@@ -193,8 +194,7 @@ static int
 cap_ls(struct Client *sptr, const char *caplist)
 {
   if (IsUnknown(sptr)) /* registration hasn't completed; suspend it... */
-    cli_unreg(sptr) |= CLIREG_CAP;
-
+    auth_cap_start(cli_auth(sptr));
   return send_caplist(sptr, 0, 0, "LS"); /* send list of capabilities */
 }
 
@@ -209,7 +209,7 @@ cap_req(struct Client *sptr, const char *caplist)
   int neg;
 
   if (IsUnknown(sptr)) /* registration hasn't completed; suspend it... */
-    cli_unreg(sptr) |= CLIREG_CAP;
+    auth_cap_start(cli_auth(sptr));
 
   memset(&set, 0, sizeof(set));
   memset(&rem, 0, sizeof(rem));
@@ -300,12 +300,7 @@ cap_end(struct Client *sptr, const char *caplist)
   if (!IsUnknown(sptr)) /* registration has completed... */
     return 0; /* so just ignore the message... */
 
-  cli_unreg(sptr) &= ~CLIREG_CAP; /* capability negotiation is now done... */
-
-  if (!cli_unreg(sptr)) /* if client is now done... */
-    return register_user(sptr, sptr, cli_name(sptr), cli_user(sptr)->username);
-
-  return 0; /* Can't do registration yet... */
+  return auth_cap_done(cli_auth(sptr));
 }
 
 static int
index d25d8a93f5ed9264de3a3cb9ff0a55f85aa5fe57..b1aef623d5db41aacc70cbf26b7f965e8187fb56 100644 (file)
@@ -85,6 +85,7 @@
 #include "ircd_log.h"
 #include "ircd_reply.h"
 #include "ircd_string.h"
+#include "s_auth.h"
 #include "send.h"
 
 /* #include <assert.h> -- Now using assert in ircd_log.h */
  */
 int mr_pass(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 {
-  const char* password = parc > 1 ? parv[1] : 0;
+  char password[BUFSIZE];
+  int arg, len;
 
   assert(0 != cptr);
   assert(cptr == sptr);
   assert(!IsRegistered(sptr));
 
+  /* Some clients (brokenly) send "PASS x y" rather than "PASS :x y"
+   * when the user enters "x y" as the password.  Unsplit arguments to
+   * work around this.
+   */
+  for (arg = 1, len = 0; arg < parc; ++arg)
+  {
+    ircd_strncpy(password + len, parv[arg], sizeof(password) - len);
+    len += strlen(parv[arg]);
+    password[len++] = ' ';
+  }
+  password[--len] = '\0';
+
   if (EmptyString(password))
     return need_more_params(cptr, "PASS");
 
-  /* TODO: For protocol negotiation */
-#if 0
-  if (ircd_strcmp(password,"PROT")==0) {
-       /* Do something here */
-  }
-#endif
   ircd_strncpy(cli_passwd(cptr), password, PASSWDLEN);
-  return 0;
+  return cli_auth(cptr) ? auth_set_password(cli_auth(cptr), password) : 0;
 }
index f66668d41432de98867ce68ad0b8b61a4081e57f..9e433958c31c761d342d2fcc7122d29906936622 100644 (file)
@@ -91,6 +91,7 @@
 #include "numeric.h"
 #include "numnicks.h"
 #include "opercmds.h"
+#include "s_auth.h"
 #include "s_user.h"
 #include "send.h"
 
@@ -163,25 +164,7 @@ int mr_pong(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 
   ClrFlag(cptr, FLAG_PINGSENT);
   cli_lasttime(cptr) = CurrentTime;
-  /*
-   * Check to see if this is a PONG :cookie reply from an
-   * unregistered user.  If so, process it. -record
-   */
-  if (0 != cli_cookie(sptr) && COOKIE_VERIFIED != cli_cookie(sptr)) {
-    if (parc > 1 && cli_cookie(sptr) == atol(parv[parc - 1])) {
-      cli_cookie(sptr) = COOKIE_VERIFIED;
-      cli_unreg(sptr) &= ~CLIREG_COOKIE; /* cookie has been returned... */
-      if (!cli_unreg(sptr)) /* no more registration tasks... */
-        /*
-         * NICK and USER OK
-         */
-        return register_user(cptr, sptr, cli_name(sptr), cli_user(sptr)->username);
-    }
-    else  
-      send_reply(sptr, SND_EXPLICIT | ERR_BADPING,
-                ":To connect, type /QUOTE PONG %u", cli_cookie(sptr));
-  }
-  return 0;
+  return (parc > 1) ? auth_set_pong(cli_auth(sptr), atol(parv[parc - 1])) : 0;
 }
 
 /*
index efbe6eb853e2ccef30aba8b684815709159e8a2e..babbd34d88641fcc237dec4889994c00feb8a3a1 100644 (file)
@@ -90,6 +90,7 @@
 #include "ircd_string.h"
 #include "numeric.h"
 #include "numnicks.h"
+#include "s_auth.h"
 #include "s_debug.h"
 #include "s_misc.h"
 #include "s_user.h"
@@ -113,7 +114,6 @@ int m_user(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
 {
   char*        username;
   const char*  info;
-  struct User* user;
 
   assert(0 != cptr);
   assert(cptr == sptr);
@@ -139,23 +139,6 @@ int m_user(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
 
   info     = (EmptyString(parv[4])) ? "No Info" : parv[4];
 
-  user = make_user(cptr);
-
-  user->server = &me;
-  ircd_strncpy(cli_info(cptr), info, REALLEN);
-
-  cli_unreg(sptr) &= ~CLIREG_USER; /* username now set */
-
-  if (!cli_unreg(sptr)) {
-    /*
-     * NICK and PONG already received, now we have USER...
-     */
-    return register_user(cptr, sptr, cli_name(sptr), username);
-  }
-  else {
-    ircd_strncpy(user->username, username, USERLEN);
-    ircd_strncpy(user->host, cli_sockhost(cptr), HOSTLEN);
-  }
-  return 0;
+  return auth_set_user(cli_auth(cptr), username, info);
 }
 
index 3aa6da47dee2eaff448bdcaa9488ecb6412d2c8b..15ccb1335b649a133a626301c9fcc12b8fcd9ee7 100644 (file)
  *     released, the server does not know it exists and does not process
  *     any messages from it.
  *     --Bleep  Thomas Helvey <tomh@inxpress.net>
+ *
+ *  December 26, 2005 - Rewrite the flag handling and integrate that with
+ *     an IRCnet-style IAuth protocol.
+ *     -- Michael Poole
  */
 /** @file
  * @brief Implementation of DNS and ident lookups.
@@ -32,6 +36,7 @@
 #include "config.h"
 
 #include "s_auth.h"
+#include "class.h"
 #include "client.h"
 #include "IPcheck.h"
 #include "ircd.h"
 #include "ircd_features.h"
 #include "ircd_log.h"
 #include "ircd_osdep.h"
+#include "ircd_reply.h"
 #include "ircd_snprintf.h"
 #include "ircd_string.h"
 #include "list.h"
 #include "numeric.h"
 #include "querycmds.h"
+#include "random.h"
 #include "res.h"
 #include "s_bsd.h"
+#include "s_conf.h"
 #include "s_debug.h"
 #include "s_misc.h"
+#include "s_user.h"
 #include "send.h"
-#include "struct.h"
-#include "sys.h"               /* TRUE bleah */
 
-#include <netdb.h>             /* struct hostent */
+#include <errno.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <errno.h>
 #include <fcntl.h>
-/* #include <assert.h> -- Now using assert in ircd_log.h */
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 
+/** Pending operations during registration. */
+enum AuthRequestFlag {
+    AR_AUTH_PENDING,    /**< ident connecting or waiting for response */
+    AR_DNS_PENDING,     /**< dns request sent, waiting for response */
+    AR_CAP_PENDING,     /**< in middle of CAP negotiations */
+    AR_NEEDS_PONG,      /**< user has not PONGed */
+    AR_NEEDS_USER,      /**< user must send USER command */
+    AR_NEEDS_NICK,      /**< user must send NICK command */
+    AR_LAST_SCAN = AR_NEEDS_NICK, /**< maximum flag to scan through */
+    AR_IAUTH_PENDING,   /**< iauth request sent, waiting for response */
+    AR_IAUTH_HURRY,     /**< we told iauth to hurry up */
+    AR_IAUTH_USERNAME,  /**< iauth sent a username (preferred or forced) */
+    AR_IAUTH_FUSERNAME, /**< iauth sent a forced username */
+    AR_NUM_FLAGS
+};
+
+DECLARE_FLAGSET(AuthRequestFlags, AR_NUM_FLAGS);
+
+/** Stores registration state of a client. */
+struct AuthRequest {
+  struct AuthRequest* next;       /**< linked list node ptr */
+  struct AuthRequest* prev;       /**< linked list node ptr */
+  struct Client*      client;     /**< pointer to client struct for request */
+  struct irc_sockaddr local;      /**< local endpoint address */
+  struct irc_in_addr  original;   /**< original client IP address */
+  struct Socket       socket;     /**< socket descriptor for auth queries */
+  struct Timer        timeout;    /**< timeout timer for auth queries */
+  struct AuthRequestFlags flags;  /**< current state of request */
+  unsigned int        cookie;     /**< cookie the user must PONG */
+  unsigned short      port;       /**< client's remote port number */
+};
+
 /** Array of message text (with length) pairs for AUTH status
- * messages.  Indexed using #ReportType. */
+ * messages.  Indexed using #ReportType.
+ */
 static struct {
   const char*  message;
   unsigned int length;
@@ -73,7 +111,6 @@ static struct {
 #define MSG(STR) { STR, sizeof(STR) - 1 }
   MSG("NOTICE AUTH :*** Looking up your hostname\r\n"),
   MSG("NOTICE AUTH :*** Found your hostname\r\n"),
-  MSG("NOTICE AUTH :*** Found your hostname, cached\r\n"),
   MSG("NOTICE AUTH :*** Couldn't look up your hostname\r\n"),
   MSG("NOTICE AUTH :*** Checking Ident\r\n"),
   MSG("NOTICE AUTH :*** Got ident response\r\n"),
@@ -88,7 +125,6 @@ static struct {
 typedef enum {
   REPORT_DO_DNS,
   REPORT_FIN_DNS,
-  REPORT_FIN_DNSC,
   REPORT_FAIL_DNS,
   REPORT_DO_ID,
   REPORT_FIN_ID,
@@ -101,357 +137,383 @@ typedef enum {
 #define sendheader(c, r) \
    send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
 
-static void release_auth_client(struct Client* client);
-void free_auth_request(struct AuthRequest* auth);
-
-/** Verify that a hostname is valid, i.e., only contains characters
- * valid for a hostname and that a hostname is not too long.
- * @param host Hostname to check.
- * @param maxlen Maximum length of hostname, not including NUL terminator.
- * @return Non-zero if the hostname is valid.
- */
-static int
-auth_verify_hostname(const char *host, int maxlen)
+/** Enumeration of IAuth connection flags. */
+enum IAuthFlag
 {
-  int i;
-
-  /* Walk through the host name */
-  for (i = 0; host[i]; i++)
-    /* If it's not a hostname character or if it's too long, return false */
-    if (!IsHostChar(host[i]) || i >= maxlen)
-      return 0;
-
-  return 1; /* it's a valid hostname */
-}
+  IAUTH_BLOCKED,                        /**< socket buffer full */
+  IAUTH_CLOSING,                        /**< candidate to be disposed */
+  /* The following flags are controlled by iauth's "O" options command. */
+  IAUTH_ADDLINFO,                       /**< Send additional info
+                                         * (password and username). */
+  IAUTH_FIRST_OPTION = IAUTH_ADDLINFO,  /**< First flag that is a policy option. */
+  IAUTH_REQUIRED,                       /**< IAuth completion required for registration. */
+  IAUTH_TIMEOUT,                        /**< Refuse new connections if IAuth is behind. */
+  IAUTH_EXTRAWAIT,                      /**< Give IAuth extra time to answer. */
+  IAUTH_UNDERNET,                       /**< Enable Undernet extensions. */
+  IAUTH_LAST_FLAG                       /**< total number of flags */
+};
+/** Declare a bitset structure indexed by IAuthFlag. */
+DECLARE_FLAGSET(IAuthFlags, IAUTH_LAST_FLAG);
+
+/** Describes state of an IAuth connection. */
+struct IAuth {
+  struct MsgQ i_sendQ;                  /**< messages queued to send */
+  struct Socket i_socket;               /**< main socket to iauth */
+  struct Socket i_stderr;               /**< error socket for iauth */
+  struct IAuthFlags i_flags;            /**< connection state/status/flags */
+  uint64_t i_recvB;                     /**< bytes received */
+  uint64_t i_sendB;                     /**< bytes sent */
+  time_t started;                       /**< time that this instance was started */
+  unsigned int i_recvM;                 /**< messages received */
+  unsigned int i_sendM;                 /**< messages sent */
+  unsigned int i_count;                 /**< characters used in i_buffer */
+  unsigned int i_errcount;              /**< characters used in i_errbuf */
+  int i_debug;                          /**< debug level */
+  char i_buffer[BUFSIZE+1];             /**< partial unprocessed line from server */
+  char i_errbuf[BUFSIZE+1];             /**< partial unprocessed error line */
+  char *i_version;                      /**< iauth version string */
+  struct SLink *i_config;               /**< configuration string list */
+  struct SLink *i_stats;                /**< statistics string list */
+  char **i_argv;                        /**< argument list */
+};
 
-/** Timeout a given auth request.
- * @param ev A timer event whose associated data is the expired struct
- * AuthRequest.
+/** Return whether flag \a flag is set on \a iauth. */
+#define IAuthHas(iauth, flag) FlagHas(&iauth->i_flags, flag)
+/** Set flag \a flag on \a iauth. */
+#define IAuthSet(iauth, flag) FlagSet(&iauth->i_flags, flag)
+/** Clear flag \a flag from \a iauth. */
+#define IAuthClr(iauth, flag) FlagClr(&iauth->i_flags, flag)
+/** Get connected flag for \a iauth. */
+#define i_GetConnected(iauth) (s_fd(i_socket(iauth)) > -1)
+
+/** Return socket event generator for \a iauth. */
+#define i_socket(iauth) (&(iauth)->i_socket)
+/** Return stderr socket for \a iauth. */
+#define i_stderr(iauth) (&(iauth)->i_stderr)
+/** Return outbound message queue for \a iauth. */
+#define i_sendQ(iauth) (&(iauth)->i_sendQ)
+/** Return debug level for \a iauth. */
+#define i_debug(iauth) (iauth->i_debug)
+
+/** Active instance of IAuth. */
+struct IAuth *iauth;
+
+static void iauth_sock_callback(struct Event *ev);
+static void iauth_stderr_callback(struct Event *ev);
+static int sendto_iauth(struct Client *cptr, const char *format, ...);
+static int preregister_user(struct Client *cptr);
+typedef int (*iauth_cmd_handler)(struct IAuth *iauth, struct Client *cli, char *params);
+
+/** Set username for user associated with \a auth.
+ * @param[in] auth Client authorization request to work on.
+ * @return Zero if client is kept, CPTR_KILLED if client rejected.
  */
-static void auth_timeout_callback(struct Event* ev)
+static int auth_set_username(struct AuthRequest *auth)
 {
-  struct AuthRequest* auth;
-
-  assert(0 != ev_timer(ev));
-  assert(0 != t_data(ev_timer(ev)));
-
-  auth = (struct AuthRequest*) t_data(ev_timer(ev));
-
-  if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
-    auth->flags &= ~AM_TIMEOUT;
-
-    if (!(auth->flags & AM_FREE_MASK)) {
-      Debug((DEBUG_LIST, "Freeing auth from timeout callback; %p [%p]", auth,
-            ev_timer(ev)));
-      MyFree(auth); /* done with it, finally */
-    }
-  } else {
-    assert(ev_type(ev) == ET_EXPIRE);
-
-    destroy_auth_request(auth, 1);
+  struct Client *sptr = auth->client;
+  struct User   *user = cli_user(sptr);
+  char *d;
+  char *s;
+  int   rlen = USERLEN;
+  int   killreason;
+  short upper = 0;
+  short lower = 0;
+  short pos = 0;
+  short leadcaps = 0;
+  short other = 0;
+  short digits = 0;
+  short digitgroups = 0;
+  char  ch;
+  char  last;
+
+  if (FlagHas(&auth->flags, AR_IAUTH_USERNAME))
+  {
+      ircd_strncpy(cli_user(sptr)->username, cli_username(sptr), USERLEN);
   }
-}
-
-/** Handle socket I/O activity.
- * @param ev A socket event whos associated data is the active struct
- * AuthRequest.
- */
-static void auth_sock_callback(struct Event* ev)
-{
-  struct AuthRequest* auth;
-
-  assert(0 != ev_socket(ev));
-  assert(0 != s_data(ev_socket(ev)));
-
-  auth = (struct AuthRequest*) s_data(ev_socket(ev));
-
-  switch (ev_type(ev)) {
-  case ET_DESTROY: /* being destroyed */
-    auth->flags &= ~AM_SOCKET;
-
-    if (!(auth->flags & AM_FREE_MASK)) {
-      Debug((DEBUG_LIST, "Freeing auth from sock callback; %p [%p]", auth,
-            ev_socket(ev)));
-      MyFree(auth); /* done with it finally */
+  else
+  {
+    /* Copy username from source to destination.  Since they may be the
+     * same, and we may prefix with a '~', use a buffer character (ch)
+     * to hold the next character to copy.
+     */
+    s = IsIdented(sptr) ? cli_username(sptr) : user->username;
+    last = *s++;
+    d = user->username;
+    if (HasFlag(sptr, FLAG_DOID) && !IsIdented(sptr))
+    {
+      *d++ = '~';
+      --rlen;
     }
-    break;
-
-  case ET_CONNECT: /* socket connection completed */
-    Debug((DEBUG_LIST, "Connection completed for auth %p [%p]; sending query",
-          auth, ev_socket(ev)));
-    socket_state(&auth->socket, SS_CONNECTED);
-    send_auth_query(auth);
-    break;
-
-  case ET_READ: /* socket is readable */
-  case ET_EOF: /* end of file on socket */
-  case ET_ERROR: /* error on socket */
-    Debug((DEBUG_LIST, "Auth socket %p [%p] readable", auth, ev_socket(ev)));
-    read_auth_reply(auth);
-    break;
+    while (last && !IsCntrl(last) && rlen--)
+    {
+      ch = *s++;
+      *d++ = IsUserChar(last) ? last : '_';
+      last = (ch != '~') ? ch : '_';
+    }
+    *d = 0;
+  }
 
-  default:
-    assert(0 && "Unrecognized event in auth_socket_callback().");
-    break;
+  /* If username is empty or just ~, reject. */
+  if ((user->username[0] == '\0')
+      || ((user->username[0] == '~') && (user->username[1] == '\0')))
+    return exit_client(sptr, sptr, &me, "USER: Bogus userid.");
+
+  /* Check for K- or G-line. */
+  killreason = find_kill(sptr);
+  if (killreason) {
+    ServerStats->is_ref++;
+    return exit_client(sptr, sptr, &me,
+                       (killreason == -1 ? "K-lined" : "G-lined"));
   }
-}
 
-/** Stop an auth request completely.
- * @param auth The struct AuthRequest to cancel.
- * @param send_reports If non-zero, report the failure to the user and
- * resolver log.
- */
-void destroy_auth_request(struct AuthRequest* auth, int send_reports)
-{
-  if (IsDoingAuth(auth)) {
-    if (-1 < auth->fd) {
-      close(auth->fd);
-      auth->fd = -1;
-      socket_del(&auth->socket);
+  if (!FlagHas(&auth->flags, AR_IAUTH_FUSERNAME))
+  {
+    /* Check for mixed case usernames, meaning probably hacked.  Jon2 3-94
+     * Explanations of rules moved to where it is checked     Entrope 2-06
+     */
+    s = d = user->username + (user->username[0] == '~');
+    for (last = '\0';
+         (ch = *d++) != '\0';
+         pos++, last = ch)
+    {
+      if (IsLower(ch))
+      {
+        lower++;
+      }
+      else if (IsUpper(ch))
+      {
+        upper++;
+        /* Accept caps as leading if we haven't seen lower case or digits yet. */
+        if ((leadcaps || pos == 0) && !lower && !digits)
+          leadcaps++;
+      }
+      else if (IsDigit(ch))
+      {
+        digits++;
+        if (pos == 0 || !IsDigit(last))
+        {
+          digitgroups++;
+          /* If more than two groups of digits, reject. */
+          if (digitgroups > 2)
+            goto badid;
+        }
+      }
+      else if (ch == '-' || ch == '_' || ch == '.')
+      {
+        other++;
+        /* If -_. exist at start, consecutively, or more than twice, reject. */
+        if (pos == 0 || last == '-' || last == '_' || last == '.' || other > 2)
+          goto badid;
+      }
+      else /* All other punctuation is rejected. */
+        goto badid;
     }
 
-    if (send_reports && IsUserPort(auth->client))
-      sendheader(auth->client, REPORT_FAIL_ID);
+    /* If mixed case, first must be capital, but no more than three;
+     * but if three capitals, they must all be leading. */
+    if (lower && upper && (!leadcaps || leadcaps > 3 ||
+                           (upper > 2 && upper > leadcaps)))
+      goto badid;
+    /* If two different groups of digits, one must be either at the
+     * start or end. */
+    if (digitgroups == 2 && !(IsDigit(s[0]) || IsDigit(ch)))
+      goto badid;
+    /* Must have at least one letter. */
+    if (!lower && !upper)
+      goto badid;
+    /* Final character must not be punctuation. */
+    if (!IsAlnum(ch))
+      goto badid;
   }
 
-  if (IsDNSPending(auth)) {
-    delete_resolver_queries(auth);
-    if (send_reports && IsUserPort(auth->client))
-      sendheader(auth->client, REPORT_FAIL_DNS);
-  }
+  return 0;
 
-  if (send_reports) {
-    log_write(LS_RESOLVER, L_INFO, 0, "DNS/AUTH timeout %s",
-             get_client_name(auth->client, HIDE_IP));
-    release_auth_client(auth->client);
-  }
+badid:
+  /* If we confirmed their username, and it is what they claimed,
+   * accept it. */
+  if (IsIdented(sptr) && !strcmp(cli_username(sptr), user->username))
+    return 0;
 
-  free_auth_request(auth);
+  ServerStats->is_ref++;
+  send_reply(sptr, SND_EXPLICIT | ERR_INVALIDUSERNAME,
+             ":Your username is invalid.");
+  send_reply(sptr, SND_EXPLICIT | ERR_INVALIDUSERNAME,
+             ":Connect with your real username, in lowercase.");
+  send_reply(sptr, SND_EXPLICIT | ERR_INVALIDUSERNAME,
+             ":If your mail address were foo@bar.com, your username "
+             "would be foo.");
+  return exit_client(sptr, sptr, &me, "USER: Bad username");
 }
 
-/** Allocate a new auth request.
- * @param client The client being looked up.
- * @return The newly allocated auth request.
+/** Check whether an authorization request is complete.
+ * This means that no flags from 0 to #AR_LAST_SCAN are set on \a auth.
+ * If #AR_IAUTH_PENDING is set, optionally go into "hurry" state.  If
+ * 0 through #AR_LAST_SCAN and #AR_IAUTH_PENDING are all clear,
+ * destroy \a auth, clear the password, set the username, and register
+ * the client.
+ * @param[in] auth Authorization request to check.
+ * @param[in] send_reports Passed to destroy_auth_request() if \a auth
+ *   is complete.
+ * @return Zero if client is kept, CPTR_KILLED if client rejected.
  */
-static struct AuthRequest* make_auth_request(struct Client* client)
+static int check_auth_finished(struct AuthRequest *auth, int send_reports)
 {
-  struct AuthRequest* auth = 
-               (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
-  assert(0 != auth);
-  memset(auth, 0, sizeof(struct AuthRequest));
-  auth->flags   = AM_TIMEOUT;
-  auth->fd      = -1;
-  auth->client  = client;
-  cli_auth(client) = auth;
-  timer_add(timer_init(&auth->timeout), auth_timeout_callback, (void*) auth,
-           TT_RELATIVE, feature_int(FEAT_AUTH_TIMEOUT));
-  return auth;
-}
+  enum AuthRequestFlag flag;
+  int res;
 
-/** Clean up auth request allocations (event loop objects, etc).
- * @param auth The request to clean up.
- */
-void free_auth_request(struct AuthRequest* auth)
-{
-  if (-1 < auth->fd) {
-    close(auth->fd);
-    Debug((DEBUG_LIST, "Deleting auth socket for %p", auth->client));
-    socket_del(&auth->socket);
-  }
-  Debug((DEBUG_LIST, "Deleting auth timeout timer for %p", auth->client));
-  timer_del(&auth->timeout);
-}
+  /* Check non-iauth registration blocking flags. */
+  for (flag = 0; flag <= AR_LAST_SCAN; ++flag)
+    if (FlagHas(&auth->flags, flag))
+    {
+      Debug((DEBUG_INFO, "Auth %p [%d] still has flag %d", auth,
+             cli_fd(auth->client), flag));
+      return 0;
+    }
 
-/** Release auth client from auth system.  This adds the client into
- * the local client lists so it can be read by the main io processing
- * loop.
- * @param client The client to release.
- */
-static void release_auth_client(struct Client* client)
-{
-  assert(0 != client);
-  cli_auth(client) = 0;
-  cli_lasttime(client) = cli_since(client) = CurrentTime;
-  if (cli_fd(client) > HighestFd)
-    HighestFd = cli_fd(client);
-  LocalClientArray[cli_fd(client)] = client;
+  /* Check if iauth is done. */
+  if (FlagHas(&auth->flags, AR_IAUTH_PENDING))
+  {
+    /* Switch auth request to hurry-up state. */
+    if (!FlagHas(&auth->flags, AR_IAUTH_HURRY))
+    {
+      struct ConfItem* aconf;
+
+      /* Set "hurry" flag in auth request. */
+      FlagSet(&auth->flags, AR_IAUTH_HURRY);
+
+      /* Do preliminary assignment to connection class. */
+      if (preregister_user(auth->client))
+        return CPTR_KILLED;
+
+      /* Check password now (to avoid challenge/response conflicts). */
+      aconf = cli_confs(auth->client)->value.aconf;
+      if (!EmptyString(aconf->passwd)
+          && strcmp(cli_passwd(auth->client), aconf->passwd))
+      {
+        ServerStats->is_ref++;
+        send_reply(auth->client, ERR_PASSWDMISMATCH);
+        return exit_client(auth->client, auth->client, &me, "Bad Password");
+      }
 
-  add_client_to_list(client);
-  socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE);
-  Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
-         cli_username(client), cli_sockhost(client), cli_sock_ip(client)));
-}
+      /* If iauth wants it, send notification. */
+      if (IAuthHas(iauth, IAUTH_UNDERNET))
+        sendto_iauth(auth->client, "H %s", ConfClass(aconf));
 
-/** Terminate a client's connection due to auth failure.
- * @param auth The client to terminate.
- */
-static void auth_kill_client(struct AuthRequest* auth)
-{
-  assert(0 != auth);
+      /* If iauth wants it, give client more time. */
+      if (IAuthHas(iauth, IAUTH_EXTRAWAIT))
+        timer_chg(&auth->timeout, TT_RELATIVE, feature_int(FEAT_AUTH_TIMEOUT));
+    }
 
-  if (IsDNSPending(auth))
-    delete_resolver_queries(auth);
-  IPcheck_disconnect(auth->client);
-  Count_unknowndisconnects(UserStats);
-  cli_auth(auth->client) = 0;
-  free_client(auth->client);
-  free_auth_request(auth);
+    Debug((DEBUG_INFO, "Auth %p [%d] still has flag %d", auth,
+           cli_fd(auth->client), AR_IAUTH_PENDING));
+    return 0;
+  }
+
+  destroy_auth_request(auth, send_reports);
+  if (!IsUserPort(auth->client))
+    return 0;
+  memset(cli_passwd(auth->client), 0, sizeof(cli_passwd(auth->client)));
+  res = auth_set_username(auth);
+  if (res == 0)
+      res = register_user(auth->client, auth->client);
+  return res;
 }
 
-/** Handle a complete DNS lookup.  Send the client on it's way to a
- * connection completion, regardless of success or failure -- unless
- * there was a mismatch and KILL_IPMISMATCH is set.
- * @param vptr The pending struct AuthRequest.
- * @param hp Pointer to the DNS reply (or NULL, if lookup failed).
+/** Verify that a hostname is valid, i.e., only contains characters
+ * valid for a hostname and that a hostname is not too long.
+ * @param host Hostname to check.
+ * @param maxlen Maximum length of hostname, not including NUL terminator.
+ * @return Non-zero if the hostname is valid.
  */
-static void auth_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name)
+static int
+auth_verify_hostname(const char *host, int maxlen)
 {
-  struct AuthRequest* auth = (struct AuthRequest*) vptr;
-  assert(auth);
-  /*
-   * need to do this here so auth_kill_client doesn't
-   * try have the resolver delete the query it's about
-   * to delete anyways. --Bleep
-   */
-  ClearDNSPending(auth);
+  int i;
 
-  if (addr) {
-    /*
-     * Verify that the host to ip mapping is correct both ways and that
-     * the ip#(s) for the socket is listed for the host.
-     */
-    if (irc_in_addr_cmp(addr, &cli_ip(auth->client))) {
-      if (IsUserPort(auth->client))
-        sendheader(auth->client, REPORT_IP_MISMATCH);
-      sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
-                          cli_sock_ip(auth->client), h_name,
-                          ircd_ntoa(addr));
-      if (feature_bool(FEAT_KILL_IPMISMATCH)) {
-       auth_kill_client(auth);
-       return;
-      }
-    }
-    else if (!auth_verify_hostname(h_name, HOSTLEN))
-    {
-      if (IsUserPort(auth->client))
-        sendheader(auth->client, REPORT_INVAL_DNS);
-    }
-    else
-    {
-      ircd_strncpy(cli_sockhost(auth->client), h_name, HOSTLEN);
-      if (IsUserPort(auth->client))
-        sendheader(auth->client, REPORT_FIN_DNS);
-    }
-  }
-  else {
-    /*
-     * this should have already been done by s_bsd.c in add_connection
-     *
-     * strcpy(auth->client->sockhost, auth->client->sock_ip);
-     */
-    if (IsUserPort(auth->client))
-      sendheader(auth->client, REPORT_FAIL_DNS);
-  }
-  if (!IsDoingAuth(auth)) {
-    release_auth_client(auth->client);
-    free_auth_request(auth);
-  }
+  /* Walk through the host name */
+  for (i = 0; host[i]; i++)
+    /* If it's not a hostname character or if it's too long, return false */
+    if (!IsHostChar(host[i]) || i >= maxlen)
+      return 0;
+
+  return 1; /* it's a valid hostname */
 }
 
-/** Handle auth send errors.
- * @param auth The request that saw the failure.
- * @param kill If non-zero, a critical error; close the client's connection.
+/** Assign a client to a connection class.
+ * @param[in] cptr Client to assign to a class.
+ * @return Zero if client is kept, CPTR_KILLED if rejected.
  */
-static void auth_error(struct AuthRequest* auth, int kill)
+static int preregister_user(struct Client *cptr)
 {
-  ++ServerStats->is_abad;
-
-  assert(0 != auth);
-  close(auth->fd);
-  auth->fd = -1;
-  socket_del(&auth->socket);
-
-  if (IsUserPort(auth->client))
-    sendheader(auth->client, REPORT_FAIL_ID);
-
-  if (kill) {
-    /*
-     * we can't read the client info from the client socket,
-     * close the client connection and free the client
-     * Need to do this before we ClearAuth(auth) so we know
-     * which list to remove the query from. --Bleep
-     */
-    auth_kill_client(auth);
-    return;
-  }
+  static time_t last_too_many1;
+  static time_t last_too_many2;
 
-  ClearAuth(auth);
+  ircd_strncpy(cli_user(cptr)->host, cli_sockhost(cptr), HOSTLEN);
+  ircd_strncpy(cli_user(cptr)->realhost, cli_sockhost(cptr), HOSTLEN);
 
-  if (!IsDNSPending(auth)) {
-    release_auth_client(auth->client);
-    free_auth_request(auth);
+  switch (conf_check_client(cptr))
+  {
+  case ACR_OK:
+    break;
+  case ACR_NO_AUTHORIZATION:
+    sendto_opmask_butone(0, SNO_UNAUTH, "Unauthorized connection from %s.",
+                         get_client_name(cptr, HIDE_IP));
+    ++ServerStats->is_ref;
+    return exit_client(cptr, cptr, &me,
+                       "No Authorization - use another server");
+  case ACR_TOO_MANY_IN_CLASS:
+    sendto_opmask_butone_ratelimited(0, SNO_TOOMANY, &last_too_many1,
+                                     "Too many connections in class %s for %s.",
+                                     get_client_class(cptr),
+                                     get_client_name(cptr, SHOW_IP));
+    ++ServerStats->is_ref;
+    return exit_client(cptr, cptr, &me,
+                       "Sorry, your connection class is full - try "
+                       "again later or try another server");
+  case ACR_TOO_MANY_FROM_IP:
+    sendto_opmask_butone_ratelimited(0, SNO_TOOMANY, &last_too_many2,
+                                     "Too many connections from same IP for %s.",
+                                     get_client_name(cptr, SHOW_IP));
+    ++ServerStats->is_ref;
+    return exit_client(cptr, cptr, &me,
+                       "Too many connections from your host");
+  case ACR_ALREADY_AUTHORIZED:
+    /* Can this ever happen? */
+  case ACR_BAD_SOCKET:
+    ++ServerStats->is_ref;
+    IPcheck_connect_fail(cptr);
+    return exit_client(cptr, cptr, &me, "Unknown error -- Try again");
   }
+  return 0;
 }
 
-/** Flag the client to show an attempt to contact the ident server on
- * the client's host.  Should the connect or any later phase of the
- * identifying process fail, it is aborted and the user is given a
- * username of "unknown".
- * @param auth The request for which to start the ident lookup.
- * @return Non-zero on success; zero if unable to start the lookup.
+/** Send the ident server a query giving "theirport , ourport". The
+ * write is only attempted *once* so it is deemed to be a fail if the
+ * entire write doesn't write all the data given.  This shouldn't be a
+ * problem since the socket should have a write buffer far greater
+ * than this message to store it in should problems arise. -avalon
+ * @param[in] auth The request to send.
  */
-static int start_auth_query(struct AuthRequest* auth)
+static void send_auth_query(struct AuthRequest* auth)
 {
-  struct irc_sockaddr remote_addr;
-  struct irc_sockaddr local_addr;
-  int                fd;
-  IOResult           result;
+  char               authbuf[32];
+  unsigned int       count;
 
   assert(0 != auth);
-  assert(0 != auth->client);
 
-  /*
-   * get the local address of the client and bind to that to
-   * make the auth request.  This used to be done only for
-   * ifdef VIRTUAL_HOST, but needs to be done for all clients
-   * since the ident request must originate from that same address--
-   * and machines with multiple IP addresses are common now
-   */
-  memset(&local_addr, 0, sizeof(local_addr));
-  os_get_sockname(cli_fd(auth->client), &local_addr);
-  local_addr.port = 0;
-  fd = os_socket(&local_addr, SOCK_STREAM, "auth query");
-  if (fd < 0) {
-    ++ServerStats->is_abad;
-    return 0;
-  }
-  if (IsUserPort(auth->client))
-    sendheader(auth->client, REPORT_DO_ID);
-  memcpy(&remote_addr.addr, &cli_ip(auth->client), sizeof(remote_addr.addr));
-  remote_addr.port = 113;
+  ircd_snprintf(0, authbuf, sizeof(authbuf), "%hu , %hu\r\n",
+                auth->port, auth->local.port);
 
-  if ((result = os_connect_nonb(fd, &remote_addr)) == IO_FAILURE ||
-      !socket_add(&auth->socket, auth_sock_callback, (void*) auth,
-                 result == IO_SUCCESS ? SS_CONNECTED : SS_CONNECTING,
-                 SOCK_EVENT_READABLE, fd)) {
-    ServerStats->is_abad++;
-    /*
-     * No error report from this...
-     */
-    close(fd);
+  if (IO_SUCCESS != os_send_nonb(s_fd(&auth->socket), authbuf, strlen(authbuf), &count)) {
+    close(s_fd(&auth->socket));
+    socket_del(&auth->socket);
+    s_fd(&auth->socket) = -1;
+    ++ServerStats->is_abad;
     if (IsUserPort(auth->client))
       sendheader(auth->client, REPORT_FAIL_ID);
-    return 0;
+    FlagClr(&auth->flags, AR_AUTH_PENDING);
+    check_auth_finished(auth, 0);
   }
-
-  auth->flags |= AM_SOCKET;
-  auth->fd = fd;
-
-  SetAuthConnect(auth);
-  if (result == IO_SUCCESS)
-    send_auth_query(auth); /* this does a SetAuthPending(auth) for us */
-
-  return 1;
 }
 
 /** Enum used to index ident reply fields in a human-readable way. */
@@ -464,7 +526,7 @@ enum IdentReplyFields {
 };
 
 /** Parse an ident reply line and extract the userid from it.
- * @param reply The ident reply line.
+ * @param[in] reply The ident reply line.
  * @return The userid, or NULL on parse failure.
  */
 static char* check_ident_reply(char* reply)
@@ -531,132 +593,1452 @@ static char* check_ident_reply(char* reply)
     if (IsSpace(*end) || '@' == *end || ':' == *end)
       break;
   }
-  *end = '\0'; 
+  *end = '\0';
   return token;
 }
 
-/** Starts auth (identd) and dns queries for a client.
- * @param client The client for which to start queries.
+/** Read the reply (if any) from the ident server we connected to.  We
+ * only give it one shot, if the reply isn't good the first time fail
+ * the authentication entirely. --Bleep
+ * @param[in] auth The request to read.
  */
-void start_auth(struct Client* client)
+static void read_auth_reply(struct AuthRequest* auth)
 {
-  struct AuthRequest* auth = 0;
-
-  assert(0 != client);
+  char*        username = 0;
+  unsigned int len;
+  /*
+   * rfc1453 sez we MUST accept 512 bytes
+   */
+  char   buf[BUFSIZE + 1];
 
-  auth = make_auth_request(client);
   assert(0 != auth);
+  assert(0 != auth->client);
+  assert(auth == cli_auth(auth->client));
 
-  Debug((DEBUG_INFO, "Beginning auth request on client %p", client));
-
-  if (!feature_bool(FEAT_NODNS)) {
-    if (irc_in_addr_is_loopback(&cli_ip(client)))
-      strcpy(cli_sockhost(client), cli_name(&me));
-    else {
-      if (IsUserPort(auth->client))
-       sendheader(client, REPORT_DO_DNS);
-      gethost_byaddr(&cli_ip(client), auth_dns_callback, auth);
-      SetDNSPending(auth);
-    }
+  if (IO_SUCCESS == os_recv_nonb(s_fd(&auth->socket), buf, BUFSIZE, &len)) {
+    buf[len] = '\0';
+    Debug((DEBUG_INFO, "Auth %p [%d] reply: %s", auth, cli_fd(auth->client), buf));
+    username = check_ident_reply(buf);
+    Debug((DEBUG_INFO, "Username: %s", username));
   }
 
-  if (start_auth_query(auth)) {
-    Debug((DEBUG_LIST, "identd query for %p initiated successfully",
-          auth->client));
-  } else if (IsDNSPending(auth)) {
-    Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
-          "waiting on DNS", auth->client));
+  Debug((DEBUG_INFO, "Deleting auth [%d] socket %p", auth, cli_fd(auth->client)));
+  close(s_fd(&auth->socket));
+  socket_del(&auth->socket);
+  s_fd(&auth->socket) = -1;
+
+  if (EmptyString(username)) {
+    if (IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FAIL_ID);
+    ++ServerStats->is_abad;
   } else {
-    Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
-          "no DNS pending; releasing immediately", auth->client));
-    free_auth_request(auth);
-    release_auth_client(client);
+    if (IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FIN_ID);
+    ++ServerStats->is_asuc;
+    if (!FlagHas(&auth->flags, AR_IAUTH_USERNAME)) {
+      ircd_strncpy(cli_username(auth->client), username, USERLEN);
+      SetGotId(auth->client);
+    }
+    if (IAuthHas(iauth, IAUTH_UNDERNET))
+      sendto_iauth(auth->client, "u %s", username);
   }
+
+  FlagClr(&auth->flags, AR_AUTH_PENDING);
+  check_auth_finished(auth, 0);
 }
 
-/** Send the ident server a query giving "theirport , ourport". The
- * write is only attempted *once* so it is deemed to be a fail if the
- * entire write doesn't write all the data given.  This shouldn't be a
- * problem since the socket should have a write buffer far greater
- * than this message to store it in should problems arise. -avalon
- * @param auth The request to send.
+/** Handle socket I/O activity.
+ * @param[in] ev A socket event whos associated data is the active
+ *   struct AuthRequest.
  */
-void send_auth_query(struct AuthRequest* auth)
+static void auth_sock_callback(struct Event* ev)
 {
-  struct irc_sockaddr us;
-  struct irc_sockaddr them;
-  char               authbuf[32];
-  unsigned int       count;
+  struct AuthRequest* auth;
+
+  assert(0 != ev_socket(ev));
+  assert(0 != s_data(ev_socket(ev)));
+
+  auth = (struct AuthRequest*) s_data(ev_socket(ev));
+
+  switch (ev_type(ev)) {
+  case ET_DESTROY: /* being destroyed */
+    break;
+
+  case ET_CONNECT: /* socket connection completed */
+    Debug((DEBUG_INFO, "Connection completed for auth %p [%d]; sending query",
+           auth, cli_fd(auth->client)));
+    socket_state(&auth->socket, SS_CONNECTED);
+    send_auth_query(auth);
+    break;
+
+  case ET_READ: /* socket is readable */
+  case ET_EOF: /* end of file on socket */
+  case ET_ERROR: /* error on socket */
+    Debug((DEBUG_INFO, "Auth socket %p [%p] readable", auth, ev_socket(ev)));
+    read_auth_reply(auth);
+    break;
+
+  default:
+    assert(0 && "Unrecognized event in auth_socket_callback().");
+    break;
+  }
+}
+
+/** Stop an auth request completely.
+ * @param[in] auth The struct AuthRequest to cancel.
+ * @param[in] send_reports If non-zero, report the failure to the user.
+ */
+void destroy_auth_request(struct AuthRequest* auth, int send_reports)
+{
+  Debug((DEBUG_INFO, "Deleting auth request for %p", auth->client));
+
+  if (FlagHas(&auth->flags, AR_AUTH_PENDING)) {
+    if (send_reports && IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FAIL_ID);
+  }
+
+  if (FlagHas(&auth->flags, AR_DNS_PENDING)) {
+    delete_resolver_queries(auth);
+    if (send_reports && IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FAIL_DNS);
+  }
+
+  if (-1 < s_fd(&auth->socket)) {
+    close(s_fd(&auth->socket));
+    socket_del(&auth->socket);
+    s_fd(&auth->socket) = -1;
+  }
+
+  timer_del(&auth->timeout);
+  cli_auth(auth->client) = NULL;
+}
+
+/** Timeout a given auth request.
+ * @param[in] ev A timer event whose associated data is the expired
+ *   struct AuthRequest.
+ */
+static void auth_timeout_callback(struct Event* ev)
+{
+  struct AuthRequest* auth;
+
+  assert(0 != ev_timer(ev));
+  assert(0 != t_data(ev_timer(ev)));
+
+  auth = (struct AuthRequest*) t_data(ev_timer(ev));
+
+  if (ev_type(ev) == ET_EXPIRE) {
+    /* Report the timeout in the log. */
+    log_write(LS_RESOLVER, L_INFO, 0, "Registration timeout %s",
+              get_client_name(auth->client, HIDE_IP));
+    /* Tell iauth if we will let the client on. */
+    if (FlagHas(&auth->flags, AR_IAUTH_PENDING)
+        && !IAuthHas(iauth, IAUTH_REQUIRED))
+    {
+      sendto_iauth(auth->client, "T");
+      FlagClr(&auth->flags , AR_IAUTH_PENDING);
+    }
+    /* Try to register the client. */
+    check_auth_finished(auth, 1);
+    /* If that failed, kick them off. */
+    if (!IsUser(auth->client))
+      exit_client(auth->client, auth->client, &me, "Authorization timed out");
+  }
+}
+
+/** Handle a complete DNS lookup.  Send the client on it's way to a
+ * connection completion, regardless of success or failure -- unless
+ * there was a mismatch and KILL_IPMISMATCH is set.
+ * @param[in] vptr The pending struct AuthRequest.
+ * @param[in] addr IP address being resolved.
+ * @param[in] h_name Resolved name, or NULL if lookup failed.
+ */
+static void auth_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name)
+{
+  struct AuthRequest* auth = (struct AuthRequest*) vptr;
+  assert(0 != auth);
+
+  FlagClr(&auth->flags, AR_DNS_PENDING);
+  if (!addr) {
+    /* DNS entry was missing for the IP. */
+    if (IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FAIL_DNS);
+    sendto_iauth(auth->client, "d");
+  } else if (irc_in_addr_cmp(addr, &cli_ip(auth->client))
+             && irc_in_addr_cmp(addr, &auth->original)) {
+    /* IP for hostname did not match client's IP. */
+    sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
+                         cli_sock_ip(auth->client), h_name,
+                         ircd_ntoa(addr));
+    if (IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_IP_MISMATCH);
+    /* Clear DNS pending flag so free_client doesn't ask the resolver
+     * to delete the query that just finished.
+     */
+    if (feature_bool(FEAT_KILL_IPMISMATCH)) {
+      IPcheck_disconnect(auth->client);
+      Count_unknowndisconnects(UserStats);
+      free_client(auth->client);
+    }
+  } else if (!auth_verify_hostname(h_name, HOSTLEN)) {
+    /* Hostname did not look valid. */
+    if (IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_INVAL_DNS);
+    sendto_iauth(auth->client, "d");
+  } else {
+    /* Hostname and mappings checked out. */
+    if (IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FIN_DNS);
+    ircd_strncpy(cli_sockhost(auth->client), h_name, HOSTLEN);
+    sendto_iauth(auth->client, "N %s", h_name);
+  }
+  check_auth_finished(auth, 0);
+}
+
+/** Flag the client to show an attempt to contact the ident server on
+ * the client's host.  Should the connect or any later phase of the
+ * identifying process fail, it is aborted and the user is given a
+ * username of "unknown".
+ * @param[in] auth The request for which to start the ident lookup.
+ */
+static void start_auth_query(struct AuthRequest* auth)
+{
+  struct irc_sockaddr remote_addr;
+  struct irc_sockaddr local_addr;
+  int                 fd;
+  IOResult            result;
 
   assert(0 != auth);
   assert(0 != auth->client);
 
-  if (!os_get_sockname(cli_fd(auth->client), &us) ||
-      !os_get_peername(cli_fd(auth->client), &them)) {
-    auth_error(auth, 1);
+  /*
+   * get the local address of the client and bind to that to
+   * make the auth request.  This used to be done only for
+   * ifdef VIRTUAL_HOST, but needs to be done for all clients
+   * since the ident request must originate from that same address--
+   * and machines with multiple IP addresses are common now
+   */
+  memcpy(&local_addr, &auth->local, sizeof(local_addr));
+  local_addr.port = 0;
+  memcpy(&remote_addr.addr, &cli_ip(auth->client), sizeof(remote_addr.addr));
+  remote_addr.port = 113;
+  fd = os_socket(&local_addr, SOCK_STREAM, "auth query");
+  if (fd < 0) {
+    ++ServerStats->is_abad;
+    if (IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FAIL_ID);
+    return;
+  }
+  if (IsUserPort(auth->client))
+    sendheader(auth->client, REPORT_DO_ID);
+
+  if ((result = os_connect_nonb(fd, &remote_addr)) == IO_FAILURE ||
+      !socket_add(&auth->socket, auth_sock_callback, (void*) auth,
+                  result == IO_SUCCESS ? SS_CONNECTED : SS_CONNECTING,
+                  SOCK_EVENT_READABLE, fd)) {
+    ++ServerStats->is_abad;
+    if (IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FAIL_ID);
+    close(fd);
+    return;
+  }
+
+  FlagSet(&auth->flags, AR_AUTH_PENDING);
+  if (result == IO_SUCCESS)
+    send_auth_query(auth);
+}
+
+/** Initiate DNS lookup for a client.
+ * @param[in] auth The auth request for which to start the DNS lookup.
+ */
+static void start_dns_query(struct AuthRequest *auth)
+{
+  if (feature_bool(FEAT_NODNS)) {
+    sendto_iauth(auth->client, "d");
     return;
   }
-  ircd_snprintf(0, authbuf, sizeof(authbuf), "%u , %u\r\n",
-               (unsigned int) them.port,
-               (unsigned int) us.port);
 
-  if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
-    ClearAuthConnect(auth);
-    SetAuthPending(auth);
+  if (irc_in_addr_is_loopback(&cli_ip(auth->client))) {
+    strcpy(cli_sockhost(auth->client), cli_name(&me));
+    sendto_iauth(auth->client, "N %s", cli_sockhost(auth->client));
+    return;
   }
-  else
-    auth_error(auth, 0);
+
+  if (IsUserPort(auth->client))
+    sendheader(auth->client, REPORT_DO_DNS);
+
+  FlagSet(&auth->flags, AR_DNS_PENDING);
+  gethost_byaddr(&cli_ip(auth->client), auth_dns_callback, auth);
 }
 
+/** Initiate IAuth check for a client.
+ * @param[in] auth The auth request for which to star the IAuth check.
+ */
+static void start_iauth_query(struct AuthRequest *auth)
+{
+  FlagSet(&auth->flags, AR_IAUTH_PENDING);
+  if (!sendto_iauth(auth->client, "C %s %hu %s %hu",
+                    cli_sock_ip(auth->client), auth->port,
+                    ircd_ntoa(&auth->local.addr), auth->local.port))
+    FlagClr(&auth->flags, AR_IAUTH_PENDING);
+}
 
-/** Read the reply (if any) from the ident server we connected to.  We
- * only give it one shot, if the reply isn't good the first time fail
- * the authentication entirely. --Bleep
- * @param auth The request to read.
+/** Starts auth (identd) and dns queries for a client.
+ * @param[in] client The client for which to start queries.
  */
-void read_auth_reply(struct AuthRequest* auth)
+void start_auth(struct Client* client)
 {
-  char*        username = 0;
-  unsigned int len;
+  struct irc_sockaddr remote;
+  struct AuthRequest* auth;
+
+  assert(0 != client);
+  Debug((DEBUG_INFO, "Beginning auth request on client %p", client));
+
+  /* Register with event handlers. */
+  cli_lasttime(client) = CurrentTime;
+  cli_since(client) = CurrentTime;
+  if (cli_fd(client) > HighestFd)
+    HighestFd = cli_fd(client);
+  LocalClientArray[cli_fd(client)] = client;
+  add_client_to_list(client);
+  socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE);
+
+  /* Allocate the AuthRequest. */
+  auth = MyCalloc(1, sizeof(*auth));
+  assert(0 != auth);
+  auth->client = client;
+  cli_auth(client) = auth;
+  s_fd(&auth->socket) = -1;
+  timer_add(timer_init(&auth->timeout), auth_timeout_callback, (void*) auth,
+            TT_RELATIVE, feature_int(FEAT_AUTH_TIMEOUT));
+
+  /* Try to get socket endpoint addresses. */
+  if (!os_get_sockname(cli_fd(client), &auth->local)
+      || !os_get_peername(cli_fd(client), &remote)) {
+    ++ServerStats->is_abad;
+    if (IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FAIL_ID);
+    IPcheck_disconnect(auth->client);
+    Count_unknowndisconnects(UserStats);
+    free_client(auth->client);
+    return;
+  }
+  auth->port = remote.port;
+
+  /* Try to start DNS lookup. */
+  start_dns_query(auth);
+
+  /* Try to start ident lookup. */
+  start_auth_query(auth);
+
+  /* Set required client inputs for users. */
+  if (IsUserPort(client)) {
+    cli_user(client) = make_user(client);
+    cli_user(client)->server = &me;
+    FlagSet(&auth->flags, AR_NEEDS_USER);
+    FlagSet(&auth->flags, AR_NEEDS_NICK);
+
+    /* Try to start iauth lookup. */
+    start_iauth_query(auth);
+  }
+
+  /* Check which auth events remain pending. */
+  check_auth_finished(auth, 0);
+}
+
+/** Mark that a user has PONGed while unregistered.
+ * @param[in] auth Authorization request for client.
+ * @param[in] cookie PONG cookie value sent by client.
+ * @return Zero if client should be kept, CPTR_KILLED if rejected.
+ */
+int auth_set_pong(struct AuthRequest *auth, unsigned int cookie)
+{
+  assert(auth != NULL);
+  if (!FlagHas(&auth->flags, AR_NEEDS_PONG))
+    return 0;
+  if (cookie != auth->cookie)
+  {
+    send_reply(auth->client, SND_EXPLICIT | ERR_BADPING,
+               ":To connect, type /QUOTE PONG %u", auth->cookie);
+    return 0;
+  }
+  FlagClr(&auth->flags, AR_NEEDS_PONG);
+  return check_auth_finished(auth, 0);
+}
+
+/** Record a user's claimed username and userinfo.
+ * @param[in] auth Authorization request for client.
+ * @param[in] username Client's asserted username.
+ * @param[in] userinfo Client's asserted self-description.
+ * @return Zero if client should be kept, CPTR_KILLED if rejected.
+ */
+int auth_set_user(struct AuthRequest *auth, const char *username, const char *userinfo)
+{
+  struct Client *cptr;
+
+  assert(auth != NULL);
+  if (FlagHas(&auth->flags, AR_IAUTH_HURRY))
+    return 0;
+  FlagClr(&auth->flags, AR_NEEDS_USER);
+  cptr = auth->client;
+  ircd_strncpy(cli_info(cptr), userinfo, REALLEN);
+  ircd_strncpy(cli_user(cptr)->username, username, USERLEN);
+  ircd_strncpy(cli_user(cptr)->host, cli_sockhost(cptr), HOSTLEN);
+  if (IAuthHas(iauth, IAUTH_UNDERNET))
+    sendto_iauth(cptr, "U %s %s", username, userinfo);
+  else if (IAuthHas(iauth, IAUTH_ADDLINFO))
+    sendto_iauth(cptr, "U %s", username);
+  return check_auth_finished(auth, 0);
+}
+
+/** Handle authorization-related aspects of initial nickname selection.
+ * This is called after verifying that the nickname is available.
+ * @param[in] auth Authorization request for client.
+ * @param[in] nickname Client's requested nickname.
+ * @return Zero if client should be kept, CPTR_KILLED if rejected.
+ */
+int auth_set_nick(struct AuthRequest *auth, const char *nickname)
+{
+  assert(auth != NULL);
+  FlagClr(&auth->flags, AR_NEEDS_NICK);
   /*
-   * rfc1453 sez we MUST accept 512 bytes
+   * If the client hasn't gotten a cookie-ping yet,
+   * choose a cookie and send it. -record!jegelhof@cloud9.net
    */
-  char   buf[BUFSIZE + 1];
+  if (!auth->cookie) {
+    do {
+      auth->cookie = ircrandom();
+    } while (!auth->cookie);
+    sendrawto_one(auth->client, "PING :%u", auth->cookie);
+    FlagSet(&auth->flags, AR_NEEDS_PONG);
+  }
+  if (IAuthHas(iauth, IAUTH_UNDERNET))
+    sendto_iauth(auth->client, "n %s", nickname);
+  return check_auth_finished(auth, 0);
+}
 
-  assert(0 != auth);
-  assert(0 != auth->client);
-  assert(auth == cli_auth(auth->client));
+/** Record a user's password.
+ * @param[in] auth Authorization request for client.
+ * @param[in] password Client's password.
+ * @return Zero if client should be kept, CPTR_KILLED if rejected.
+ */
+int auth_set_password(struct AuthRequest *auth, const char *password)
+{
+  assert(auth != NULL);
+  if (IAuthHas(iauth, IAUTH_ADDLINFO))
+    sendto_iauth(auth->client, "P %s", password);
+  return 0;
+}
 
-  if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
-    buf[len] = '\0';
-    Debug((DEBUG_LIST, "Auth %p [%p] reply: %s", auth, &auth->socket, buf));
-    username = check_ident_reply(buf);
-    Debug((DEBUG_LIST, "Username: %s", username));
+/** Send exit notification for \a cptr to iauth.
+ * @param[in] cptr Client who is exiting.
+ */
+void auth_send_exit(struct Client *cptr)
+{
+  sendto_iauth(cptr, "D");
+}
+
+/** Mark that a user has started capabilities negotiation.
+ * This blocks authorization until auth_cap_done() is called.
+ * @param[in] auth Authorization request for client.
+ * @return Zero if client should be kept, CPTR_KILLED if rejected.
+ */
+int auth_cap_start(struct AuthRequest *auth)
+{
+  assert(auth != NULL);
+  FlagSet(&auth->flags, AR_CAP_PENDING);
+  return 0;
+}
+
+/** Mark that a user has completed capabilities negotiation.
+ * This unblocks authorization if auth_cap_start() was called.
+ * @param[in] auth Authorization request for client.
+ * @return Zero if client should be kept, CPTR_KILLED if rejected.
+ */
+int auth_cap_done(struct AuthRequest *auth)
+{
+  assert(auth != NULL);
+  FlagClr(&auth->flags, AR_CAP_PENDING);
+  return check_auth_finished(auth, 0);
+}
+
+/** Attempt to spawn the process for an IAuth instance.
+ * @param[in] iauth IAuth descriptor.
+ * @param[in] automatic If non-zero, apply sanity checks against
+ *   excessive automatic restarts.
+ * @return 0 on success, non-zero on failure.
+ */
+int iauth_do_spawn(struct IAuth *iauth, int automatic)
+{
+  pid_t cpid;
+  int s_io[2];
+  int s_err[2];
+  int res;
+
+  if (automatic && CurrentTime - iauth->started < 5)
+  {
+    sendto_opmask_butone(NULL, SNO_AUTH, "IAuth crashed fast, leaving it dead.");
+    return -1;
   }
 
-  close(auth->fd);
-  auth->fd = -1;
-  Debug((DEBUG_LIST, "Deleting auth [%p] socket %p", auth, &auth->socket));
-  socket_del(&auth->socket);
-  ClearAuth(auth);
+  /* Record time we tried to spawn the iauth process. */
+  iauth->started = CurrentTime;
+
+  /* Attempt to allocate a pair of sockets. */
+  res = os_socketpair(s_io);
+  if (res)
+    return errno;
+
+  /* Mark the parent's side of the pair (element 0) as non-blocking. */
+  res = os_set_nonblocking(s_io[0]);
+  if (!res) {
+    res = errno;
+    close(s_io[1]);
+    close(s_io[0]);
+    return res;
+  }
 
-  if (!EmptyString(username)) {
-    ircd_strncpy(cli_username(auth->client), username, USERLEN);
-    /*
-     * Not needed, struct is zeroed by memset
-     * auth->client->username[USERLEN] = '\0';
+  /* Initialize the socket structure to talk to the child. */
+  res = socket_add(i_socket(iauth), iauth_sock_callback, iauth,
+                   SS_CONNECTED, SOCK_EVENT_READABLE, s_io[0]);
+  if (!res) {
+    res = errno;
+    close(s_io[1]);
+    close(s_io[0]);
+    return res;
+  }
+
+  /* Allocate another pair for stderr. */
+  res = os_socketpair(s_err);
+  if (res) {
+    res = errno;
+    socket_del(i_socket(iauth));
+    close(s_io[1]);
+    close(s_io[0]);
+    return res;
+  }
+
+  /* Mark parent side of this pair non-blocking, too. */
+  res = os_set_nonblocking(s_err[0]);
+  if (!res) {
+    res = errno;
+    close(s_err[1]);
+    close(s_err[0]);
+    socket_del(i_socket(iauth));
+    close(s_io[1]);
+    close(s_io[0]);
+    return res;
+  }
+
+  /* And set up i_stderr(iauth). */
+  res = socket_add(i_stderr(iauth), iauth_stderr_callback, iauth,
+                   SS_CONNECTED, SOCK_EVENT_READABLE, s_err[0]);
+  if (!res) {
+    res = errno;
+    close(s_err[1]);
+    close(s_err[0]);
+    socket_del(i_socket(iauth));
+    close(s_io[1]);
+    close(s_io[0]);
+    return res;
+  }
+
+  /* Attempt to fork a child process. */
+  cpid = fork();
+  if (cpid < 0) {
+    /* Error forking the child, still in parent. */
+    res = errno;
+    socket_del(i_stderr(iauth));
+    close(s_err[1]);
+    close(s_err[0]);
+    socket_del(i_socket(iauth));
+    close(s_io[1]);
+    close(s_io[0]);
+    return res;
+  }
+
+  if (cpid > 0) {
+    /* We are the parent process.  Close the child's sockets. */
+    close(s_io[1]);
+    close(s_err[1]);
+    /* Send our server name (supposedly for proxy checking purposes)
+     * and maximum number of connections (for allocation hints).
+     * Need to use conf_get_local() since &me may not be fully
+     * initialized the first time we run.
      */
-    SetGotId(auth->client);
-    ++ServerStats->is_asuc;
-    if (IsUserPort(auth->client))
-      sendheader(auth->client, REPORT_FIN_ID);
+    sendto_iauth(NULL, "M %s %d", conf_get_local()->name, MAXCONNECTIONS);
+    /* Indicate success (until the child dies). */
+    return 0;
   }
-  else {
-    ++ServerStats->is_abad;
+
+  /* We are the child process.
+   * Duplicate our end of the socket to stdin, stdout and stderr.
+   * Then close all the higher-numbered FDs and exec the process.
+   */
+  if (dup2(s_io[1], 0) == 0
+      && dup2(s_io[1], 1) == 1
+      && dup2(s_err[1], 2) == 2) {
+    close_connections(0);
+    execvp(iauth->i_argv[0], iauth->i_argv);
   }
 
-  if (!IsDNSPending(auth)) {
-    release_auth_client(auth->client);
-    free_auth_request(auth);
+  /* If we got here, something was seriously wrong. */
+  exit(EXIT_FAILURE);
+}
+
+/** See if an %IAuth program must be spawned.
+ * If a process is already running with the specified options, keep it.
+ * Otherwise spawn a new child process to perform the %IAuth function.
+ * @param[in] argc Number of parameters to use when starting process.
+ * @param[in] argv Array of parameters to start process.
+ * @return 0 on failure, 1 on new process, 2 on reuse of existing process.
+ */
+int auth_spawn(int argc, char *argv[])
+{
+  int ii;
+
+  if (iauth) {
+    int same = 1;
+
+    /* Check that incoming arguments all match pre-existing arguments. */
+    for (ii = 0; same && (ii < argc); ++ii) {
+      if (NULL == iauth->i_argv[ii]
+          || 0 != strcmp(iauth->i_argv[ii], argv[ii]))
+        same = 0;
+    }
+    /* Check that we have no more pre-existing arguments. */
+    if (iauth->i_argv[ii])
+      same = 0;
+    /* If they are the same and still connected, clear the "closing" flag and exit.*/
+    if (same && i_GetConnected(iauth)) {
+      IAuthClr(iauth, IAUTH_CLOSING);
+      return 2;
+    }
+    /* Deallocate old argv elements. */
+    for (ii = 0; iauth->i_argv[ii]; ++ii)
+      MyFree(iauth->i_argv[ii]);
+    MyFree(iauth->i_argv);
+  }
+
+  /* Need to initialize a new connection. */
+  iauth = MyCalloc(1, sizeof(*iauth));
+  msgq_init(i_sendQ(iauth));
+  /* Populate iauth's argv array. */
+  iauth->i_argv = MyCalloc(argc + 1, sizeof(iauth->i_argv[0]));
+  for (ii = 0; ii < argc; ++ii)
+    DupString(iauth->i_argv[ii], argv[ii]);
+  iauth->i_argv[ii] = NULL;
+  /* Try to spawn it, and handle the results. */
+  if (iauth_do_spawn(iauth, 0))
+    return 0;
+  IAuthClr(iauth, IAUTH_CLOSING);
+  return 1;
+}
+
+/** Mark all %IAuth connections as closing. */
+void auth_mark_closing(void)
+{
+  if (iauth)
+    IAuthSet(iauth, IAUTH_CLOSING);
+}
+
+/** Complete disconnection of an %IAuth connection.
+ * @param[in] iauth %Connection to fully close.
+ */
+static void iauth_disconnect(struct IAuth *iauth)
+{
+  if (!i_GetConnected(iauth))
+    return;
+
+  /* Close main socket. */
+  close(s_fd(i_socket(iauth)));
+  socket_del(i_socket(iauth));
+  s_fd(i_socket(iauth)) = -1;
+
+  /* Close error socket. */
+  close(s_fd(i_stderr(iauth)));
+  socket_del(i_stderr(iauth));
+  s_fd(i_stderr(iauth)) = -1;
+}
+
+/** Close all %IAuth connections marked as closing. */
+void auth_close_unused(void)
+{
+  if (iauth && IAuthHas(iauth, IAUTH_CLOSING)) {
+    int ii;
+    iauth_disconnect(iauth);
+    if (iauth->i_argv) {
+      for (ii = 0; iauth->i_argv[ii]; ++ii)
+        MyFree(iauth->i_argv[ii]);
+      MyFree(iauth->i_argv);
+    }
+    MyFree(iauth);
+  }
+}
+
+/** Send queued output to \a iauth.
+ * @param[in] iauth Writable connection with queued data.
+ */
+static void iauth_write(struct IAuth *iauth)
+{
+  unsigned int bytes_tried, bytes_sent;
+  IOResult iores;
+
+  if (IAuthHas(iauth, IAUTH_BLOCKED))
+    return;
+  while (MsgQLength(i_sendQ(iauth)) > 0) {
+    iores = os_sendv_nonb(s_fd(i_socket(iauth)), i_sendQ(iauth), &bytes_tried, &bytes_sent);
+    switch (iores) {
+    case IO_SUCCESS:
+      msgq_delete(i_sendQ(iauth), bytes_sent);
+      iauth->i_sendB += bytes_sent;
+      if (bytes_tried == bytes_sent)
+        break;
+      /* If bytes_sent < bytes_tried, fall through to IO_BLOCKED. */
+    case IO_BLOCKED:
+      IAuthSet(iauth, IAUTH_BLOCKED);
+      socket_events(i_socket(iauth), SOCK_ACTION_ADD | SOCK_EVENT_WRITABLE);
+      return;
+    case IO_FAILURE:
+      iauth_disconnect(iauth);
+      return;
+    }
+  }
+  /* We were able to flush all events, so remove notification. */
+  socket_events(i_socket(iauth), SOCK_ACTION_DEL | SOCK_EVENT_WRITABLE);
+}
+
+/** Send a message to iauth.
+ * @param[in] cptr Optional client context for message.
+ * @param[in] format Format string for message.
+ * @return Non-zero on successful send or buffering, zero on failure.
+ */
+static int sendto_iauth(struct Client *cptr, const char *format, ...)
+{
+  struct VarData vd;
+  struct MsgBuf *mb;
+
+  /* Do not send requests when we have no iauth. */
+  if (!i_GetConnected(iauth))
+    return 0;
+  /* Do not send for clients in the NORMAL state. */
+  if (cptr
+      && (format[0] != 'D')
+      && (!cli_auth(cptr) || !FlagHas(&cli_auth(cptr)->flags, AR_IAUTH_PENDING)))
+    return 0;
+
+  /* Build the message buffer. */
+  vd.vd_format = format;
+  va_start(vd.vd_args, format);
+  if (0 == cptr)
+    mb = msgq_make(NULL, "-1 %v", &vd);
+  else
+    mb = msgq_make(NULL, "%d %v", cli_fd(cptr), &vd);
+  va_end(vd.vd_args);
+
+  /* Tack it onto the iauth sendq and try to write it. */
+  ++iauth->i_sendM;
+  msgq_add(i_sendQ(iauth), mb, 0);
+  iauth_write(iauth);
+  return 1;
+}
+
+/** Send text to interested operators (SNO_AUTH server notice).
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Text to send.
+ * @return Zero.
+ */
+static int iauth_cmd_snotice(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  sendto_opmask_butone(NULL, SNO_AUTH, "%s", params);
+  return 0;
+}
+
+/** Set the debug level for the session.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params String starting with an integer.
+ * @return Zero.
+ */
+static int iauth_cmd_debuglevel(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  int new_level;
+
+  new_level = atoi(params);
+  if (i_debug(iauth) > 0 || new_level > 0) {
+    /* The "ia_dbg" name is borrowed from (IRCnet) ircd. */
+    sendto_opmask_butone(NULL, SNO_AUTH, "ia_dbg = %d", new_level);
+  }
+  i_debug(iauth) = new_level;
+  return 0;
+}
+
+/** Set policy options for the session.
+ * Old policy is forgotten, and any of the following characters in \a
+ * params enable the corresponding policy:
+ * \li A IAUTH_ADDLINFO
+ * \li R IAUTH_REQUIRED
+ * \li T IAUTH_TIMEOUT
+ * \li W IAUTH_EXTRAWAIT
+ * \li U IAUTH_UNDERNET
+ *
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Zero or more policy options.
+ * @return Zero.
+ */
+static int iauth_cmd_policy(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  enum IAuthFlag flag;
+  char *p;
+
+  /* Erase old policy first. */
+  for (flag = IAUTH_FIRST_OPTION; flag < IAUTH_LAST_FLAG; ++flag)
+    IAuthClr(iauth, flag);
+
+  /* Parse new policy set. */
+  for (p = params; *p; p++) switch (*p) {
+  case 'A': IAuthSet(iauth, IAUTH_ADDLINFO); break;
+  case 'R': IAuthSet(iauth, IAUTH_REQUIRED); break;
+  case 'T': IAuthSet(iauth, IAUTH_TIMEOUT); break;
+  case 'W': IAuthSet(iauth, IAUTH_EXTRAWAIT); break;
+  case 'U': IAuthSet(iauth, IAUTH_UNDERNET); break;
+  }
+
+  /* Optionally notify operators. */
+  if (i_debug(iauth) > 0)
+    sendto_opmask_butone(NULL, SNO_AUTH, "iauth options: %s", params);
+  return 0;
+}
+
+/** Set the iauth program version number.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Version number or name.
+ * @return Zero.
+ */
+static int iauth_cmd_version(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  MyFree(iauth->i_version);
+  while (IsSpace(*params))
+    ++params;
+  DupString(iauth->i_version, params);
+  sendto_opmask_butone(NULL, SNO_AUTH, "iauth version %s running.", iauth->i_version);
+  return 0;
+}
+
+/** Clear cached iauth configuration information.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Parameter list (ignored).
+ * @return Zero.
+ */
+static int iauth_cmd_newconfig(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  struct SLink *head;
+  struct SLink *next;
+
+  head = iauth->i_config;
+  iauth->i_config = NULL;
+  for (; head; head = next) {
+    next = head->next;
+    MyFree(head->value.cp);
+    free_link(head);
+  }
+  sendto_opmask_butone(NULL, SNO_AUTH, "New iauth configuration.");
+  return 0;
+}
+
+/** Append iauth configuration information.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Description of configuration element.
+ * @return Zero.
+ */
+static int iauth_cmd_config(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  struct SLink *node;
+
+  if (iauth->i_config) {
+    for (node = iauth->i_config; node->next; node = node->next) ;
+    node = node->next = make_link();
+  } else {
+    node = iauth->i_config = make_link();
+  }
+  while (IsSpace(*params))
+    ++params;
+  DupString(node->value.cp, params);
+  return 0;
+}
+
+/** Clear cached iauth configuration information.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Parameter list (ignored).
+ * @return Zero.
+ */
+static int iauth_cmd_newstats(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  struct SLink *head;
+  struct SLink *next;
+
+  head = iauth->i_stats;
+  iauth->i_stats = NULL;
+  for (; head; head = next) {
+    next = head->next;
+    MyFree(head->value.cp);
+    free_link(head);
+  }
+  sendto_opmask_butone(NULL, SNO_AUTH, "New iauth statistics.");
+  return 0;
+}
+
+/** Append iauth statistics information.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Statistics element.
+ * @return Zero.
+ */
+static int iauth_cmd_stats(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  struct SLink *node;
+  if (iauth->i_stats) {
+    for (node = iauth->i_stats; node->next; node = node->next) ;
+    node = node->next = make_link();
+  } else {
+    node = iauth->i_stats = make_link();
+  }
+  while (IsSpace(*params))
+    ++params;
+  DupString(node->value.cp, params);
+  return 0;
+}
+
+/** Set client's username to a trusted string even if it breaks the rules.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Forced username.
+ * @return One.
+ */
+static int iauth_cmd_username_forced(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  assert(cli_auth(cli) != NULL);
+  FlagClr(&cli_auth(cli)->flags, AR_AUTH_PENDING);
+  if (!EmptyString(params)) {
+    ircd_strncpy(cli_username(cli), params, USERLEN);
+    SetGotId(cli);
+    FlagSet(&cli_auth(cli)->flags, AR_IAUTH_USERNAME);
+    FlagSet(&cli_auth(cli)->flags, AR_IAUTH_FUSERNAME);
   }
+  return 1;
+}
+
+/** Set client's username to a trusted string.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Trusted username.
+ * @return One.
+ */
+static int iauth_cmd_username_good(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  assert(cli_auth(cli) != NULL);
+  FlagClr(&cli_auth(cli)->flags, AR_AUTH_PENDING);
+  if (!EmptyString(params)) {
+    ircd_strncpy(cli_username(cli), params, USERLEN);
+    SetGotId(cli);
+    FlagSet(&cli_auth(cli)->flags, AR_IAUTH_USERNAME);
+  }
+  return 1;
+}
+
+/** Set client's username to an untrusted string.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Untrusted username.
+ * @return One.
+ */
+static int iauth_cmd_username_bad(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  assert(cli_auth(cli) != NULL);
+  FlagClr(&cli_auth(cli)->flags, AR_AUTH_PENDING);
+  if (!EmptyString(params))
+    ircd_strncpy(cli_user(cli)->username, params, USERLEN);
+  return 1;
+}
+
+/** Set client's hostname.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params New hostname for client.
+ * @return Non-zero if \a cli authorization should be checked for completion.
+ */
+static int iauth_cmd_hostname(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  struct AuthRequest *auth;
+
+  if (EmptyString(params)) {
+    sendto_iauth(cli, "E Missing :Missing hostname parameter");
+    return 0;
+  }
+
+  auth = cli_auth(cli);
+  assert(auth != NULL);
+
+  /* If a DNS request is pending, abort it. */
+  if (FlagHas(&auth->flags, AR_DNS_PENDING)) {
+    FlagClr(&auth->flags, AR_DNS_PENDING);
+    delete_resolver_queries(auth);
+    if (IsUserPort(cli))
+      sendheader(cli, REPORT_FIN_DNS);
+  }
+  /* Set hostname from params. */
+  ircd_strncpy(cli_sockhost(cli), params, HOSTLEN);
+  return 1;
+}
+
+/** Set client's IP address.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params New IP address for client in dotted quad or
+ *   standard IPv6 format.
+ * @return Zero.
+ */
+static int iauth_cmd_ip_address(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  struct irc_in_addr addr;
+  struct AuthRequest *auth;
+
+  /* Get AuthRequest for client. */
+  auth = cli_auth(cli);
+  assert(auth != NULL);
+
+  /* Parse the client's new IP address. */
+  if (!ircd_aton(&addr, params)) {
+    sendto_iauth(cli, "E Invalid :Unable to parse IP address [%s]", params);
+    return 0;
+  }
+
+  /* If this is the first IP override, save the client's original
+   * address in case we get a DNS response later.
+   */
+  if (!irc_in_addr_valid(&auth->original))
+    memcpy(&auth->original, &cli_ip(cli), sizeof(auth->original));
+
+  /* Undo original IP connection in IPcheck. */
+  IPcheck_connect_fail(cli);
+  ClearIPChecked(cli);
+
+  /* Update the IP and charge them as a remote connect. */
+  memcpy(&cli_ip(cli), &addr, sizeof(cli_ip(cli)));
+  IPcheck_remote_connect(cli, 0);
+
+  return 0;
+}
+
+/** Find a ConfItem structure for a named connection class.
+ * @param[in] class_name Name of configuration class to find.
+ * @return A ConfItem of type CONF_CLIENT for the class, or NULL on failure.
+ */
+static struct ConfItem *auth_find_class_conf(const char *class_name)
+{
+  static struct ConfItem *aconf_list;
+  struct ConnectionClass *class;
+  struct ConfItem *aconf;
+
+  /* Make sure the configuration class is valid. */
+  class = find_class(class_name);
+  if (!class)
+    return NULL;
+
+  /* Look for an existing ConfItem for the class. */
+  for (aconf = aconf_list; aconf; aconf = aconf->next)
+    if (aconf->conn_class == class)
+      break;
+
+  /* If no ConfItem, create one. */
+  if (!aconf) {
+    aconf = make_conf(CONF_CLIENT);
+    if (!aconf) {
+      sendto_opmask_butone(NULL, SNO_AUTH,
+                           "Unable to allocate ConfItem for class %s!",
+                           ConClass(class));
+      return NULL;
+    }
+    aconf->conn_class = class;
+    aconf->next = aconf_list;
+    aconf_list = aconf;
+  }
+
+  return aconf;
+}
+
+/** Accept a client in IAuth.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Optional class name for client.
+ * @return One.
+ */
+static int iauth_cmd_done_client(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  static time_t warn_time;
+
+  /* Clear iauth pending flag. */
+  assert(cli_auth(cli) != NULL);
+  FlagClr(&cli_auth(cli)->flags, AR_IAUTH_PENDING);
+
+  /* If a connection class was specified (and usable), assign the client to it. */
+  if (!EmptyString(params)) {
+    struct ConfItem *aconf;
+
+    aconf = auth_find_class_conf(params);
+    if (aconf)
+      attach_conf(cli, aconf);
+    else
+      sendto_opmask_butone_ratelimited(NULL, SNO_AUTH, &warn_time,
+                                       "iauth tried to use undefined class [%s]",
+                                       params);
+  }
+
+  return 1;
+}
+
+/** Accept a client in IAuth and assign them to an account.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Account name and optional class name for client.
+ * @return Non-zero if \a cli authorization should be checked for completion.
+ */
+static int iauth_cmd_done_account(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  char *end;
+  size_t len;
+
+  /* Sanity check. */
+  if (EmptyString(params)) {
+    sendto_iauth(cli, "E Missing :Missing account parameter");
+    return 0;
+  }
+  /* Check length of account name. */
+  len = strcspn(params, ": ");
+  if (len > ACCOUNTLEN) {
+    sendto_iauth(cli, "E Invalid :Account parameter too long");
+    return 0;
+  }
+  /* If account has a creation timestamp, use it. */
+  assert(cli_user(cli) != NULL);
+  if (params[len] == ':')
+    cli_user(cli)->acc_create = strtoul(params + len + 1, &end, 10);
+  else
+    end = params + len;
+  /* Copy account name to User structure. */
+  ircd_strncpy(cli_user(cli)->account, params, ACCOUNTLEN);
+  SetAccount(cli);
+  /* Skip whitespace before next argument. */
+  while (IsSpace(*end))
+    ++end;
+  /* Fall through to the normal "done" handler. */
+  return iauth_cmd_done_client(iauth, cli, end);
+}
+
+/** Reject a client's connection.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Optional kill message.
+ * @return Zero.
+ */
+static int iauth_cmd_kill(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  if (cli_auth(cli))
+    FlagClr(&cli_auth(cli)->flags, AR_IAUTH_PENDING);
+  if (EmptyString(params))
+    params = "Access denied";
+  exit_client(cli, cli, &me, params);
+  return 0;
+}
+
+/** Send a challenge string to the client.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Challenge message for client.
+ * @return Zero.
+ */
+static int iauth_cmd_challenge(struct IAuth *iauth, struct Client *cli, char *params)
+{
+  sendrawto_one(cli, "NOTICE AUTH :*** %s", params);
+  return 0;
+}
+
+/** Read input from \a iauth.
+ * Reads up to SERVER_TCP_WINDOW bytes per pass.
+ * @param[in] iauth Readable connection.
+ */
+static void iauth_read(struct IAuth *iauth)
+{
+  static char readbuf[SERVER_TCP_WINDOW];
+  iauth_cmd_handler handler;
+  struct AuthRequest *auth;
+  struct Client *cli;
+  char *old_buffer;
+  char *params;
+  char *endp;
+  char *src;
+  unsigned int length;
+  int has_cli;
+  int res;
+  int id;
+
+  switch (os_recv_nonb(s_fd(i_socket(iauth)), readbuf, sizeof(readbuf), &length))
+  {
+  case IO_SUCCESS: break;
+  case IO_FAILURE: iauth_disconnect(iauth);
+  case IO_BLOCKED: return;
+  }
+  iauth->i_recvB += length;
+  old_buffer = iauth->i_buffer;
+  endp = old_buffer + iauth->i_count;
+  for (src = readbuf; length > 0; --length) {
+    *endp = *src++;
+    if (IsEol(*endp)) {
+      /* Terminate line, reset buffer and update statistics. */
+      *endp = '\0';
+      endp = old_buffer;
+      ++iauth->i_recvM;
+
+      /* If spammy debug, send the message to opers. */
+      if (i_debug(iauth) > 1)
+        sendto_opmask_butone(NULL, SNO_AUTH, "%s", endp);
+
+      /* Find command handler.  A lot of the handlers would be simpler
+       * with an argument splitter like in parse.c, but some commands
+       * (notably '>') do not use delimiters that way.
+       */
+      switch (*(endp = old_buffer)) {
+      case '>': handler = iauth_cmd_snotice; has_cli = 0; break;
+      case 'G': handler = iauth_cmd_debuglevel; has_cli = 0; break;
+      case 'O': handler = iauth_cmd_policy; has_cli = 0; break;
+      case 'V': handler = iauth_cmd_version; has_cli = 0; break;
+      case 'a': handler = iauth_cmd_newconfig; has_cli = 0; break;
+      case 'A': handler = iauth_cmd_config; has_cli = 0; break;
+      case 's': handler = iauth_cmd_newstats; has_cli = 0; break;
+      case 'S': handler = iauth_cmd_stats; has_cli = 0; break;
+      case 'o': handler = iauth_cmd_username_forced; has_cli = 1; break;
+      case 'U': handler = iauth_cmd_username_good; has_cli = 1; break;
+      case 'u': handler = iauth_cmd_username_bad; has_cli = 1; break;
+      case 'N': handler = iauth_cmd_hostname; has_cli = 1; break;
+      case 'I': handler = iauth_cmd_ip_address; has_cli = 1; break;
+      case 'C': handler = iauth_cmd_challenge; has_cli = 1; break;
+      case 'D': handler = iauth_cmd_done_client; has_cli = 1; break;
+      case 'R': handler = iauth_cmd_done_account; has_cli = 1; break;
+      case 'k': /* The 'k' command indicates the user should be booted
+                 * off without telling opers.  There is no way to
+                 * signal that to exit_client(), so we fall through to
+                 * the case that we do implement.
+                 */
+      case 'K': handler = iauth_cmd_kill; has_cli = 2; break;
+      case 'r': /* we handle termination directly */ continue;
+      default:  sendto_iauth(NULL, "E Garbage :[%s]", endp); continue;
+      }
+
+      /* Skip whitespace at start of arguments. */
+      while (IsSpace(*++endp)) ;
+
+      /* At this point, has_cli has one of three values:
+       * 0 - no client is identified
+       * 1 - a client is identified and must still be registering
+       * 2 - a client is identified but may be fully registered
+       */
+
+      /* Figure out how to handle the command. */
+      if (!has_cli) {
+        /* Handler does not need a client. */
+        handler(iauth, NULL, endp);
+      } else {
+        /* Try to find the client associated with the request. */
+        id = strtol(endp, &params, 10);
+        while (IsSpace(*params))
+          ++params;
+        if (id < 0 || id > HighestFd || !(cli = LocalClientArray[id])) {
+          /* Client no longer exists (or never existed). */
+          sendto_iauth(NULL, "E Gone :[%s]", params);
+        } else if ((!(auth = cli_auth(cli))
+                    || !FlagHas(&auth->flags, AR_IAUTH_PENDING))
+                   && (has_cli == 1)) {
+          /* Client is done with IAuth checks. */
+          sendto_iauth(cli, "E Done :[%s]", params);
+        } else {
+          struct irc_sockaddr addr;
+          char *orig_ip;
+
+          /* Skip whitespace before IP address. */
+          while (IsSpace(*params))
+            ++params;
+          /* Record start of IP address, then null terminate it. */
+          orig_ip = params;
+          while (!IsSpace(*params) && *params != '\0')
+            ++params;
+          if (IsSpace(*params))
+            *params++ = '\0';
+          /* Parse out client IP address and port number. */
+          res = ipmask_parse(orig_ip, &addr.addr, NULL);
+          addr.port = strtol(params, &params, 10);
+          /* Skip whitespace and optional sentinel after port number. */
+          while (IsSpace(*params))
+            ++params;
+          if (*params == ':')
+            ++params;
+          /* Check IP address and port number against expected. */
+          if (0 == res
+              || irc_in_addr_cmp(&addr.addr, &cli_ip(cli))
+              || (auth && addr.port != auth->port)) {
+            /* Report mismatch to iauth. */
+            sendto_iauth(cli, "E Mismatch :[%s] != [%s]",
+                         orig_ip, ircd_ntoa(&cli_ip(cli)));
+          } else if (handler(iauth, cli, params)) {
+            /* Handler indicated a possible state change. */
+            check_auth_finished(auth, 0);
+          }
+        }
+      }
+
+      /* Reset buffer pointer to read next line. */
+      endp = old_buffer;
+    }
+    else if (endp < old_buffer + BUFSIZE)
+      ++endp;
+  }
+  iauth->i_count = endp - old_buffer;
+}
+
+/** Handle socket activity for an %IAuth connection.
+ * @param[in] ev &Socket event; the IAuth connection is the user data
+ *   pointer for the socket.
+ */
+static void iauth_sock_callback(struct Event *ev)
+{
+  struct IAuth *iauth;
+
+  assert(0 != ev_socket(ev));
+  iauth = (struct IAuth*) s_data(ev_socket(ev));
+  assert(0 != iauth);
+
+  switch (ev_type(ev)) {
+  case ET_DESTROY:
+    /* Hm, what happened here? */
+    if (!IAuthHas(iauth, IAUTH_CLOSING))
+      iauth_do_spawn(iauth, 1);
+    break;
+  case ET_READ:
+    iauth_read(iauth);
+    break;
+  case ET_WRITE:
+    IAuthClr(iauth, IAUTH_BLOCKED);
+    iauth_write(iauth);
+    break;
+  case ET_ERROR:
+    log_write(LS_IAUTH, L_ERROR, 0, "IAuth socket error: %s", strerror(ev_data(ev)));
+    /* and fall through to the ET_EOF case */
+  case ET_EOF:
+    iauth_disconnect(iauth);
+    break;
+  default:
+    assert(0 && "Unrecognized event type");
+    break;
+  }
+}
+
+/** Read error input from \a iauth.
+ * @param[in] iauth Readable connection.
+ */
+static void iauth_read_stderr(struct IAuth *iauth)
+{
+  static char readbuf[SERVER_TCP_WINDOW];
+  unsigned int length;
+  char *sol;
+  char *eol;
+
+  /* Copy partial data to readbuf, append new data. */
+  length = iauth->i_errcount;
+  memcpy(readbuf, iauth->i_errbuf, length);
+  if (IO_SUCCESS != os_recv_nonb(s_fd(i_stderr(iauth)),
+                                 readbuf + length,
+                                 sizeof(readbuf) - length - 1,
+                                 &length))
+    return;
+  readbuf[length] = '\0';
+
+  /* Send each complete line to SNO_AUTH. */
+  for (sol = readbuf; (eol = strchr(sol, '\n')) != NULL; sol = eol + 1) {
+    *eol = '\0';
+    Debug((DEBUG_ERROR, "IAuth error: %s", sol));
+    log_write(LS_IAUTH, L_ERROR, 0, "IAuth error: %s", sol);
+    sendto_opmask_butone(NULL, SNO_AUTH, "%s", sol);
+  }
+
+  /* Put unused data back into connection's buffer. */
+  iauth->i_errcount = strlen(sol);
+  if (iauth->i_errcount > BUFSIZE)
+    iauth->i_errcount = BUFSIZE;
+  memcpy(iauth->i_errbuf, sol, iauth->i_errcount);
+}
+
+/** Handle error socket activity for an %IAuth connection.
+ * @param[in] ev &Socket event; the IAuth connection is the user data
+ *   pointer for the socket.
+ */
+static void iauth_stderr_callback(struct Event *ev)
+{
+  struct IAuth *iauth;
+
+  assert(0 != ev_socket(ev));
+  iauth = (struct IAuth*) s_data(ev_socket(ev));
+  assert(0 != iauth);
+
+  switch (ev_type(ev)) {
+  case ET_READ:
+    iauth_read_stderr(iauth);
+    break;
+  case ET_ERROR:
+    log_write(LS_IAUTH, L_ERROR, 0, "IAuth stderr error: %s", strerror(ev_data(ev)));
+    /* and fall through to the ET_EOF/ET_DESTROY case */
+  case ET_DESTROY:
+  case ET_EOF:
+    break;
+  default:
+    assert(0 && "Unrecognized event type");
+    break;
+  }
+}
+
+/** Report active iauth's configuration to \a cptr.
+ * @param[in] cptr Client requesting statistics.
+ * @param[in] sd Stats descriptor for request.
+ * @param[in] param Extra parameter from user (may be NULL).
+ */
+void report_iauth_conf(struct Client *cptr, const struct StatDesc *sd, char *param)
+{
+    struct SLink *link;
+
+    for (link = iauth->i_config; link; link = link->next)
+    {
+        send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":%s",
+                   link->value.cp);
+    }
+    send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":End of IAuth configuration.");
+}
+
+/** Report active iauth's statistics to \a cptr.
+ * @param[in] cptr Client requesting statistics.
+ * @param[in] sd Stats descriptor for request.
+ * @param[in] param Extra parameter from user (may be NULL).
+ */
+ void report_iauth_stats(struct Client *cptr, const struct StatDesc *sd, char *param)
+{
+    struct SLink *link;
+
+    for (link = iauth->i_stats; link; link = link->next)
+    {
+        send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":%s",
+                   link->value.cp);
+    }
+    send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":End of IAuth statistics.");
 }
index 0c1b230d7c8faedb989353c8f026d8641b954161..f4dde532464f939c5010f81f9855cca6a6d39d7a 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"
@@ -949,14 +949,14 @@ int rehash(struct Client *cptr, int sig)
 
   class_mark_delete();
   mark_listeners_closing();
-  iauth_mark_closing();
+  auth_mark_closing();
   close_mappings();
 
   read_configuration_file();
 
   log_reopen(); /* reopen log files */
 
-  iauth_close_unused();
+  auth_close_unused();
   close_listeners();
   class_delete_marked();         /* unless it fails */
 
index 34638546a759dcbb08dfee40031d46023575ef85..8eab44ee5e59ca346dccd5302e4b7ce28e3e1c75 100644 (file)
@@ -33,7 +33,6 @@
 #include "hash.h"
 #include "ircd.h"
 #include "ircd_alloc.h"
-#include "ircd_auth.h"
 #include "ircd_features.h"
 #include "ircd_log.h"
 #include "ircd_reply.h"
@@ -47,6 +46,7 @@
 #include "parse.h"
 #include "querycmds.h"
 #include "res.h"
+#include "s_auth.h"
 #include "s_bsd.h"
 #include "s_conf.h"
 #include "s_debug.h"
@@ -266,8 +266,6 @@ static void exit_one_client(struct Client* bcptr, const char* comment)
     assert(!IsServer(bcptr));
     /* bcptr->user->server->serv->client_list[IndexYXX(bcptr)] = NULL; */
     RemoveYXXClient(cli_user(bcptr)->server, cli_yxx(bcptr));
-    if (IsIAuthed(bcptr) || cli_iauth(bcptr))
-      iauth_exit_client(bcptr);
   }
 
   /* Remove bcptr from the client list */
@@ -379,6 +377,9 @@ int exit_client(struct Client *cptr,
 
     on_for = CurrentTime - cli_firsttime(victim);
 
+    if (IsUser(victim) || IsUserPort(victim))
+      auth_send_exit(victim);
+
     if (IsUser(victim))
       log_write(LS_USER, L_TRACE, 0, "%Tu %i %s@%s %s %s %s%s %s :%s",
                cli_firsttime(victim), on_for,
index c37630b8dea442ec732fb8305c025296a1ba0b11..bfc2d584d8a942520b30644df075ebdd00231f78 100644 (file)
@@ -43,6 +43,7 @@
 #include "numnicks.h"
 #include "querycmds.h"
 #include "res.h"
+#include "s_auth.h"
 #include "s_bsd.h"
 #include "s_conf.h"
 #include "s_debug.h"
@@ -617,6 +618,12 @@ struct StatDesc statsinfo[] = {
   { 'z', "memory", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_z,
     count_memory, 0,
     "Memory/Structure allocation information." },
+  { ' ', "iauth", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_IAUTH,
+    report_iauth_stats, 0,
+    "IAuth statistics." },
+  { ' ', "iauthconf", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_IAUTH,
+    report_iauth_conf, 0,
+    "IAuth configuration." },
   { '*', "help", STAT_FLAG_CASESENS, FEAT_LAST_F,
     stats_help, 0,
     "Send help for stats." },
index d0a621732ca70ee26d73085a9816916620f775db..8226a8e002b3e6be96f931a1501cd41454244d2f 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_features.h"
 #include "ircd_log.h"
@@ -51,6 +50,7 @@
 #include "parse.h"
 #include "querycmds.h"
 #include "random.h"
+#include "s_auth.h"
 #include "s_bsd.h"
 #include "s_conf.h"
 #include "s_debug.h"
@@ -304,40 +304,6 @@ int hunt_server_prio_cmd(struct Client *from, const char *cmd, const char *tok,
 }
 
 
-/** Copy a cleaned-up version of a username.
- * Replace all instances of '~' and "invalid" username characters
- * (!isIrcUi()) with underscores, truncating at USERLEN or the first
- * control character.  \a dest and \a source may be the same buffer.
- * @param[out] dest Destination buffer.
- * @param[in] source Source buffer.
- * @param[in] tilde If non-zero, prepend a '~' to \a dest.
- */
-static char *clean_user_id(char *dest, char *source, int tilde)
-{
-  char ch;
-  char *d = dest;
-  char *s = source;
-  int rlen = USERLEN;
-
-  ch = *s++;                        /* Store first character to copy: */
-  if (tilde)
-  {
-    *d++ = '~';                        /* If `dest' == `source', then this overwrites `ch' */
-    --rlen;
-  }
-  while (ch && !IsCntrl(ch) && rlen--)
-  {
-    char nch = *s++;        /* Store next character to copy */
-    *d++ = IsUserChar(ch) ? ch : '_';        /* This possibly overwrites it */
-    if (nch == '~')
-      ch = '_';
-    else
-      ch = nch;
-  }
-  *d = 0;
-  return dest;
-}
-
 /*
  * register_user
  *
@@ -369,29 +335,13 @@ static char *clean_user_id(char *dest, char *source, int tilde)
  *
  * @param[in] cptr Client who introduced the user.
  * @param[in,out] sptr Client who has been fully introduced.
- * @param[in] nick Client's new nickname.
- * @param[in] username Client's username.
  * @return Zero or CPTR_KILLED.
  */
-int register_user(struct Client *cptr, struct Client *sptr,
-                  const char *nick, char *username)
+int register_user(struct Client *cptr, struct Client *sptr)
 {
-  struct ConfItem* aconf;
   char*            parv[4];
   char*            tmpstr;
-  char*            tmpstr2;
-  char             c = 0;    /* not alphanum */
-  char             d = 'a';  /* not a digit */
-  short            upper = 0;
-  short            lower = 0;
-  short            pos = 0;
-  short            leadcaps = 0;
-  short            other = 0;
-  short            digits = 0;
-  short            badid = 0;
-  short            digitgroups = 0;
   struct User*     user = cli_user(sptr);
-  int              killreason;
   char             ip_base64[25];
 
   user->last = CurrentTime;
@@ -400,165 +350,8 @@ int register_user(struct Client *cptr, struct Client *sptr,
 
   if (MyConnect(sptr))
   {
-    static time_t last_too_many1;
-    static time_t last_too_many2;
-
     assert(cptr == sptr);
-    assert(cli_unreg(sptr) == 0);
-    if (!IsIAuthed(sptr)) {
-      if (iauth_active)
-        return iauth_start_client(iauth_active, sptr);
-      else
-        SetIAuthed(sptr);
-    }
-    switch (conf_check_client(sptr))
-    {
-      case ACR_OK:
-        break;
-      case ACR_NO_AUTHORIZATION:
-        sendto_opmask_butone(0, SNO_UNAUTH, "Unauthorized connection from %s.",
-                             get_client_name(sptr, HIDE_IP));
-        ++ServerStats->is_ref;
-        return exit_client(cptr, sptr, &me,
-                           "No Authorization - use another server");
-      case ACR_TOO_MANY_IN_CLASS:
-        if (CurrentTime - last_too_many1 >= (time_t) 60)
-        {
-          last_too_many1 = CurrentTime;
-          sendto_opmask_butone(0, SNO_TOOMANY, "Too many connections in "
-                               "class %s for %s.", get_client_class(sptr),
-                               get_client_name(sptr, SHOW_IP));
-        }
-        ++ServerStats->is_ref;
-        IPcheck_connect_fail(sptr);
-        return exit_client(cptr, sptr, &me,
-                           "Sorry, your connection class is full - try "
-                           "again later or try another server");
-      case ACR_TOO_MANY_FROM_IP:
-        if (CurrentTime - last_too_many2 >= (time_t) 60)
-        {
-          last_too_many2 = CurrentTime;
-          sendto_opmask_butone(0, SNO_TOOMANY, "Too many connections from "
-                               "same IP for %s.",
-                               get_client_name(sptr, SHOW_IP));
-        }
-        ++ServerStats->is_ref;
-        return exit_client(cptr, sptr, &me,
-                           "Too many connections from your host");
-      case ACR_ALREADY_AUTHORIZED:
-        /* Can this ever happen? */
-      case ACR_BAD_SOCKET:
-        ++ServerStats->is_ref;
-        IPcheck_connect_fail(sptr);
-        return exit_client(cptr, sptr, &me, "Unknown error -- Try again");
-    }
-    ircd_strncpy(user->host, cli_sockhost(sptr), HOSTLEN);
-    ircd_strncpy(user->realhost, cli_sockhost(sptr), HOSTLEN);
-    aconf = cli_confs(sptr)->value.aconf;
-
-    clean_user_id(user->username,
-                  HasFlag(sptr, FLAG_GOTID) ? cli_username(sptr) : username,
-                  HasFlag(sptr, FLAG_DOID) && !HasFlag(sptr, FLAG_GOTID));
 
-    if ((user->username[0] == '\0')
-        || ((user->username[0] == '~') && (user->username[1] == '\000')))
-      return exit_client(cptr, sptr, &me, "USER: Bogus userid.");
-
-    if (!EmptyString(aconf->passwd)
-        && strcmp(cli_passwd(sptr), aconf->passwd))
-    {
-      ServerStats->is_ref++;
-      send_reply(sptr, ERR_PASSWDMISMATCH);
-      return exit_client(cptr, sptr, &me, "Bad Password");
-    }
-    memset(cli_passwd(sptr), 0, sizeof(cli_passwd(sptr)));
-    /*
-     * following block for the benefit of time-dependent K:-lines
-     */
-    killreason = find_kill(sptr);
-    if (killreason) {
-      ServerStats->is_ref++;
-      return exit_client(cptr, sptr, &me,
-                         (killreason == -1 ? "K-lined" : "G-lined"));
-    }
-    /*
-     * Check for mixed case usernames, meaning probably hacked.  Jon2 3-94
-     * Summary of rules now implemented in this patch:         Ensor 11-94
-     * In a mixed-case name, if first char is upper, one more upper may
-     * appear anywhere.  (A mixed-case name *must* have an upper first
-     * char, and may have one other upper.)
-     * A third upper may appear if all 3 appear at the beginning of the
-     * name, separated only by "others" (-/_/.).
-     * A single group of digits is allowed anywhere.
-     * Two groups of digits are allowed if at least one of the groups is
-     * at the beginning or the end.
-     * Only one '-', '_', or '.' is allowed (or two, if not consecutive).
-     * But not as the first or last char.
-     * No other special characters are allowed.
-     * Name must contain at least one letter.
-     */
-    tmpstr2 = tmpstr = (username[0] == '~' ? &username[1] : username);
-    while (*tmpstr && !badid)
-    {
-      pos++;
-      c = *tmpstr;
-      tmpstr++;
-      if (IsLower(c))
-      {
-        lower++;
-      }
-      else if (IsUpper(c))
-      {
-        upper++;
-        if ((leadcaps || pos == 1) && !lower && !digits)
-          leadcaps++;
-      }
-      else if (IsDigit(c))
-      {
-        digits++;
-        if (pos == 1 || !IsDigit(d))
-        {
-          digitgroups++;
-          if (digitgroups > 2)
-            badid = 1;
-        }
-      }
-      else if (c == '-' || c == '_' || c == '.')
-      {
-        other++;
-        if (pos == 1)
-          badid = 1;
-        else if (d == '-' || d == '_' || d == '.' || other > 2)
-          badid = 1;
-      }
-      else
-        badid = 1;
-      d = c;
-    }
-    if (!badid)
-    {
-      if (lower && upper && (!leadcaps || leadcaps > 3 ||
-          (upper > 2 && upper > leadcaps)))
-        badid = 1;
-      else if (digitgroups == 2 && !(IsDigit(tmpstr2[0]) || IsDigit(c)))
-        badid = 1;
-      else if ((!lower && !upper) || !IsAlnum(c))
-        badid = 1;
-    }
-    if (badid && (!HasFlag(sptr, FLAG_GOTID) ||
-        strcmp(cli_username(sptr), username) != 0))
-    {
-      ServerStats->is_ref++;
-
-      send_reply(cptr, SND_EXPLICIT | ERR_INVALIDUSERNAME,
-                 ":Your username is invalid.");
-      send_reply(cptr, SND_EXPLICIT | ERR_INVALIDUSERNAME,
-                 ":Connect with your real username, in lowercase.");
-      send_reply(cptr, SND_EXPLICIT | ERR_INVALIDUSERNAME,
-                 ":If your mail address were foo@bar.com, your username "
-                 "would be foo.");
-      return exit_client(cptr, sptr, &me, "USER: Bad username");
-    }
     Count_unknownbecomesclient(sptr, UserStats);
 
     SetUser(sptr);
@@ -569,7 +362,7 @@ int register_user(struct Client *cptr, struct Client *sptr,
                feature_str(FEAT_NETWORK),
                feature_str(FEAT_PROVIDER) ? " via " : "",
                feature_str(FEAT_PROVIDER) ? feature_str(FEAT_PROVIDER) : "",
-               nick);
+               cli_name(sptr));
     /*
      * This is a duplicate of the NOTICE but see below...
      */
@@ -621,12 +414,10 @@ int register_user(struct Client *cptr, struct Client *sptr,
     }
   }
   else {
-    struct Client *acptr;
+    struct Client *acptr = user->server;
 
-    ircd_strncpy(user->username, username, USERLEN);
-    Count_newremoteclient(UserStats, user->server);
+    Count_newremoteclient(UserStats, acptr);
 
-    acptr = user->server;
     if (cli_from(acptr) != cli_from(sptr))
     {
       sendcmdto_one(&me, CMD_KILL, cptr, "%C :%s (%s != %s[%s])",
@@ -671,7 +462,8 @@ int register_user(struct Client *cptr, struct Client *sptr,
   sendcmdto_flag_serv_butone(user->server, CMD_NICK, cptr,
                              FLAG_IPV6, FLAG_LAST_FLAG,
                              "%s %d %Tu %s %s %s%s%s%s %s%s :%s",
-                             nick, cli_hopcount(sptr) + 1, cli_lastnick(sptr),
+                             cli_name(sptr), cli_hopcount(sptr) + 1,
+                             cli_lastnick(sptr),
                              user->username, user->realhost,
                              *tmpstr ? "+" : "", tmpstr, *tmpstr ? " " : "",
                              iptobase64(ip_base64, &cli_ip(sptr), sizeof(ip_base64), 1),
@@ -680,7 +472,8 @@ int register_user(struct Client *cptr, struct Client *sptr,
   sendcmdto_flag_serv_butone(user->server, CMD_NICK, cptr,
                              FLAG_LAST_FLAG, FLAG_IPV6,
                              "%s %d %Tu %s %s %s%s%s%s %s%s :%s",
-                             nick, cli_hopcount(sptr) + 1, cli_lastnick(sptr),
+                             cli_name(sptr), cli_hopcount(sptr) + 1,
+                             cli_lastnick(sptr),
                              user->username, user->realhost,
                              *tmpstr ? "+" : "", tmpstr, *tmpstr ? " " : "",
                              iptobase64(ip_base64, &cli_ip(sptr), sizeof(ip_base64), 0),
@@ -789,6 +582,7 @@ int set_nick_name(struct Client* cptr, struct Client* sptr,
 
     cli_serv(sptr)->ghost = 0;        /* :server NICK means end of net.burst */
     ircd_strncpy(cli_username(new_client), parv[4], USERLEN);
+    ircd_strncpy(cli_user(new_client)->username, parv[4], USERLEN);
     ircd_strncpy(cli_user(new_client)->host, parv[5], HOSTLEN);
     ircd_strncpy(cli_user(new_client)->realhost, parv[5], HOSTLEN);
     ircd_strncpy(cli_info(new_client), parv[parc - 1], REALLEN);
@@ -807,7 +601,7 @@ int set_nick_name(struct Client* cptr, struct Client* sptr,
       ircd_snprintf(0, cli_user(new_client)->host, HOSTLEN, "%s.%s",
         account, feature_str(FEAT_HIDDEN_HOST));
 
-    return register_user(cptr, new_client, cli_name(new_client), parv[4]);
+    return register_user(cptr, new_client);
   }
   else if ((cli_name(sptr))[0]) {
     /*
@@ -880,36 +674,9 @@ int set_nick_name(struct Client* cptr, struct Client* sptr,
   }
   else {
     /* Local client setting NICK the first time */
-
     strcpy(cli_name(sptr), nick);
-    if (!cli_user(sptr)) {
-      cli_user(sptr) = make_user(sptr);
-      cli_user(sptr)->server = &me;
-    }
     hAddClient(sptr);
-
-    cli_unreg(sptr) &= ~CLIREG_NICK; /* nickname now set */
-
-    /*
-     * If the client hasn't gotten a cookie-ping yet,
-     * choose a cookie and send it. -record!jegelhof@cloud9.net
-     */
-    if (!cli_cookie(sptr)) {
-      do {
-        cli_cookie(sptr) = (ircrandom() & 0x7fffffff);
-      } while (!cli_cookie(sptr));
-      sendrawto_one(cptr, MSG_PING " :%u", cli_cookie(sptr));
-    }
-    else if (!cli_unreg(sptr)) {
-      /*
-       * USER and PONG already received, now we have NICK.
-       * register_user may reject the client and call exit_client
-       * for it - must test this and exit m_nick too !
-       */
-      cli_lastnick(sptr) = TStime();        /* Always local client */
-      if (register_user(cptr, sptr, nick, cli_user(sptr)->username) == CPTR_KILLED)
-        return CPTR_KILLED;
-    }
+    return auth_set_nick(cli_auth(sptr), nick);
   }
   return 0;
 }
diff --git a/tools/iauth-test b/tools/iauth-test
new file mode 100755 (executable)
index 0000000..2629b52
--- /dev/null
@@ -0,0 +1,238 @@
+#! /usr/bin/perl
+# iauth-test: test script for IRC authorization (iauth) protocol
+# Copyright 2006 Michael Poole
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+require 5.008; # We assume deferred signal handlers, new in 5.008.
+use strict;
+use warnings;
+use vars qw(%pending);
+
+use Carp;       # for carp
+use Config;     # for $Config{sig_name} and $Config{sig_num}
+use FileHandle; # for autoflush method on file handles
+
+# This script is intended to help test an implementation of the iauth
+# protocol by exercising every command in the protocol and by
+# exercising most distinct combinations of commands.  It assumes IPv4
+# support in the server and POSIX real-time signal support in the OS
+# (recognized and supported by Perl).
+
+# Certain behavior is triggered by receipt of real-time signals.
+# SIGRTMIN + 0 -> Send server notice ('>').
+# SIGRTMIN + 1 -> Toggle debug level ('G').
+# SIGRTMIN + 2 -> Set policy options ('O').
+# SIGRTMIN + 3 -> Simulate config change ('a', 'A').
+# SIGRTMIN + 4 -> Simulate statistics change ('s', 'S').
+
+# In the following discussion, sX means message X from the server, and
+# iX means message X from iauth.  The hard part is the ordering of
+# various events during client registration.  This includes sC, sP,
+# sU, su, sn, sN/d, sH and sT; and o/U/u, iN, iI, iC and iD/R/k/K.
+
+# sC is first, sD/sT/iD/R/k/K is last.  If sH is sent, no more sU, su,
+# sn, sN, sd or sH messages may be sent.  If iI is sent, iN should
+# also be sent (either before or after iI).  Multiple sP, sU and iC
+# messages may be sent. Otherwse, the ordering of unrelated messages
+# from either source are not constrained, but only one message from
+# each set of alternatives may be sent.
+
+# This means the sets of commands with interesting orderings are:
+# sU, su, io/U/u
+# sN/d, iN, iI
+# sH, sT or iD/R/k/K
+
+# 127.x.y.z IP addresses are used to exercise these orderings; see the
+# %handlers variable below.
+
+sub dolog ($) {
+    print LOG "$_[0]\n";
+}
+
+sub reply ($;$$) {
+    my ($msg, $client, $extra) = @_;
+
+    if (not defined $msg) {
+        # Accept this for easier handling of client reply messages.
+        return;
+    } elsif (ref $msg eq '') {
+        $msg =~ s/^(.) ?/$1 $client->{id} $client->{ip} $client->{port} / if $client;
+        dolog "< $msg";
+        print "$msg\n";
+    } elsif (ref $msg eq 'ARRAY') {
+        grep { reply($_, $client, $extra); } @$msg;
+    } elsif (ref $msg eq 'CODE') {
+        &$msg($client, $extra);
+    } else {
+        die "Unknown reply message type.";
+    }
+}
+
+# Find the names of signals with values SIGRTMIN+1, +2, etc.
+BEGIN {
+    my @sig_name;
+    my %sig_num;
+
+    sub populate_signals () {
+        die "No sigs?"
+            unless $Config{sig_name} and $Config{sig_num};
+        my @names = split ' ', $Config{sig_name};
+        @sig_num{@names} = split ' ', $Config{sig_num};
+        foreach (@names) { $sig_name[$sig_num{$_}] ||= $_; }
+    }
+
+    sub assign_signal_handlers() {
+        my $sigrtmin = $sig_num{RTMIN};
+        die "No realtime signals?"
+            unless $sigrtmin;
+        $SIG{$sig_name[$sigrtmin+0]} = \&send_server_notice;
+        $SIG{$sig_name[$sigrtmin+1]} = \&toggle_debug_level;
+        $SIG{$sig_name[$sigrtmin+2]} = \&set_policy_options;
+        $SIG{$sig_name[$sigrtmin+3]} = \&sim_config_changed;
+        $SIG{$sig_name[$sigrtmin+4]} = \&sim_stats_change;
+    }
+}
+
+BEGIN {
+    my $debug_level = 0;
+    my $max_debug_level = 2;
+
+    sub toggle_debug_level () {
+        if (++$debug_level > $max_debug_level) {
+            $debug_level = 0;
+        }
+        reply "G $debug_level";
+    }
+}
+
+BEGIN {
+    my %rotation = (
+        '' => 'AU',
+        'AU' => 'AURTW',
+        'AURTW' => '',
+    );
+    my $policy = '';
+
+    sub set_policy_options () {
+        $policy = $rotation{$policy};
+        reply "O $policy";
+    }
+}
+
+BEGIN {
+    my $generation = 0;
+
+    sub sim_config_changed () {
+        reply "a";
+        reply "A config $generation";
+        $generation++;
+    }
+}
+
+BEGIN {
+    my $generation = 0;
+
+    sub sim_stats_change () {
+        reply "s";
+        reply "S stats $generation";
+        $generation++;
+    }
+}
+
+sub send_server_notice () {
+    reply "> Hello the server!";
+}
+
+my %handlers = (
+                # Default handliner: immediately report done.
+                'default'    => { C_reply => 'D' },
+                # 127.0.0.x: various timings for iD/iR/ik/iK.
+                '127.0.0.1'  => { C_reply => 'D' },
+                '127.0.0.2'  => { C_reply => 'R account-1' },
+                '127.0.0.3'  => { C_reply => 'k' },
+                '127.0.0.4'  => { C_reply => 'K' },
+                '127.0.0.15' => { },
+                '127.0.0.16' => { H_reply => 'D' },
+                '127.0.0.17' => { H_reply => 'R account-2' },
+                '127.0.0.18' => { H_reply => 'k' },
+                '127.0.0.19' => { H_reply => 'K' },
+                '127.0.0.32' => { T_reply => 'D' },
+                '127.0.0.33' => { T_reply => 'R account-3' },
+                '127.0.0.34' => { T_reply => 'k' },
+                '127.0.0.35' => { T_reply => 'K' },
+                # 127.0.1.x: io/iU/iu functionality.
+                '127.0.1.0'  => { C_reply => 'o forced',
+                                  H_reply => 'D' },
+                '127.0.1.1'  => { C_reply => 'U trusted',
+                                  H_reply => 'D' },
+                '127.0.1.2'  => { C_reply => 'u untrusted',
+                                  H_reply => 'D' },
+                # 127.0.2.x: iI/iN functionality.
+                '127.0.2.0'  => { C_reply => 'N iauth.assigned.host',
+                                  H_reply => 'D' },
+                '127.0.2.1'  => { C_reply => \&ip_change },
+                # 127.0.3.x: iC/sP functionality.
+                '127.0.3.0'  => { C_reply => 'C Please enter the password.',
+                                  P_reply => \&passwd_check },
+);
+
+sub handle_new_client ($$$$) {
+    my ($id, $ip, $port, $extra) = @_;
+    my $handler = $handlers{$ip} || $handlers{default};
+    my $client = { id => $id, ip => $ip, port => $port, handler => $handler };
+
+    # If we have any deferred reply handlers, we must save the client.
+    $pending{$id} = $client if grep /^[^C]_reply$/, keys %$handler;
+    reply $client->{handler}->{C_reply}, $client, $extra;
+}
+
+sub ip_change ($$) {
+    my ($client, $extra) = @_;
+    reply 'I 127.255.255.254', $client;
+    $client->{ip} = '127.255.255.254';
+    reply 'N other.assigned.host', $client;
+    reply 'D', $client;
+}
+
+sub passwd_check ($$) {
+    my ($client, $extra) = @_;
+    if ($extra eq 'secret') {
+        reply 'D', $client;
+    } else {
+        reply 'C :Bad password', $client;
+    }
+}
+
+open LOG, ">> iauth.log";
+populate_signals();
+assign_signal_handlers();
+autoflush LOG 1;
+autoflush STDOUT 1;
+autoflush STDERR 1;
+dolog "IAuth starting " . scalar(localtime(time));
+
+while (<>) {
+    my ($id, $client);
+
+    # Chomp newline and log incoming message.
+    s/\r?\n?\r?$//;
+    dolog "> $_";
+
+    # If there's an ID at the start of the line, parse it out.
+    if (s/^(\d+) //) { $id = $1; $client = $pending{$id}; }
+
+    # Figure out how to handle the command.
+    if (/^C (\S+) (\S+) (.+)$/) {
+        handle_new_client($id, $1, $2, $3);
+    } elsif (/^([DT])/ and $client) {
+        reply $client->{handler}->{"${1}_reply"}, $client;
+        delete $pending{$id};
+    } elsif (/^([d])/ and $client) {
+        reply $client->{handler}->{"${1}_reply"}, $client;
+    } elsif (/^([HNPUu]) (.+)/ and $client) {
+        reply $client->{handler}->{"${1}_reply"}, $client, $2;
+    }
+}