Tip of the day: The Security article gives hands-on tips on how to deal with drone attacks, flooding, spammers, (D)DoS and more.

Dev:Extended Bans API

From UnrealIRCd documentation wiki
Jump to navigation Jump to search

Writing a custom extended ban type is relatively easy as all you have to deal with is strings and/or return a value.

In addition to the tutorial below, you may also want to look at examples in UnrealIRCd itself (learn by copying). The built-in extban modules are in src/modules/extbans/.

Creating an extban

Similar to Dev:Channel Mode API you call the function to register your extban from MOD_INIT and you throw a struct at it like this:

MOD_INIT()
{
    ExtbanInfo extban;

    memset(&extban, 0, sizeof(extban));
    extban.name = "example";
    extban.letter = 'X'; /* only used for backwards compatibility (but required!) */
    extban.is_ok = mymod_is_ok;
    extban.conv_param = mymod_conv_param; // or set to a generic handler, like: extban_conv_param_nuh_or_extban
    extban.is_banned = mymod_is_banned;
    extban.is_banned_events = BANCHK_ALL; /* what events to listen on, eg only BANCHK_NICK, or just BANCHK_ALL */
    extban.options = EXTBOPT_ACTMODIFIER;
    
    ExtbanAdd(modinfo->handle, extban);

So basically you pass: 1) the module handle, 2) a ExtbanInfo struct

The ExtbanInfo struct contains the following (regular) variables which you may set:

  • name: name of the extban, eg if this is "example" then the extban will be "~example". No other module may use the same name (must be unique).
  • letter: specifies which letter the extban uses. This is only for backwards compatibility with older UnrealIRCd servers and clients. For example 'X' would mean +b ~X:..etc.. No other module may use the same letter (must be unique).
  • is_banned_events: one of BANCHK_*. Most use BANCHK_ALL here, but if you only want the ban to apply on for example joining then you set it to BANCHK_JOIN. You can also set BANCHK_ALL|BANCHK_TKL in which case you can use the ban in Extended server bans such as GLINE.
  • options: TODO

Other than that, the ExtbanInfo struct contains of function pointers which you must set. Only conv_param and is_banned are required, the rest is optional. See below.

conv_param

Convert input parameter to a 'correct' parameter. For example if you wish +b ~example:blah to be converted to +b ~example:blah!*@*.

Declaration of the conv_param function:

const char *conv_param(BanContext *b, Extban *extban)
Variable Type Description
param BanContext * The ban context
extban Extban * The extended ban handler - rarely used

In the BanContext *b, the following values are useful:

Variable Type Description
b->client Client * Client who issues the MODE change
b->channel Channel * Channel to which the MODE change applies
b->banstr const char * The ban string (eg: "something" for "/MODE #chan +b ~example:something")
b->what int Used to differentiate between adding and removing the mode, one of: MODE_ADD or MODE_DEL
b->ban_type ExtbanType EXBTYPE_BAN for bans (+b) and EXBTYPE_EXCEPT for excepts (+e)

You should return a string (a const char * to static storage!), or NULL if you wish to reject an invalid parameter. For better "is this parameter okay?" checking you can also use the is_ok function (in addition).

Example code:

/* Silly example that converts ~example:somestring where somestring will be of max 8 characters in size. */
const char *mymod_conv_param(BanContext *b, Extban *extban)
{
    static char ret[8 + 1];

    if (!*para)
        return NULL; /* empty string is rejected (eg "~example:") */

    strlcpy(ret, param, sizeof(ret));
    return ret;
}

There is also some help from the UnrealIRCd side:

  • instead of writing your own conv_param you can use extban_conv_param_nuh which will accept and convert input to a nick!user@host (resulting in something like ~example:nick!user@host)
  • similarly you can use extban_conv_param_nuh_or_extban which will allow a nick!user@host or another extban, so to allow extban stacking (eg: ~example:~realname:*somerealname*)

is_ok

Access and parameter checking. This is completely optional and actually often not used. See also conv_param.

Declaration of the is_ok function:

int is_ok(BanContext *b)
Variable Type Description
param BanContext * The ban context

In the BanContext *b, the following values are useful:

Variable Type Description
b->client Client * Client who issues the MODE change
b->channel Channel * Channel to which the MODE change applies
b->banstr const char * The ban string (eg: "something" for "/MODE #chan +b ~example:something")
b->is_ok_check ExtbanCheck Check type, one of EXBCHK_*. Explained later.
b->what int Used to differentiate between adding and removing the mode, one of: MODE_ADD or MODE_DEL
b->ban_type ExtbanType EXBTYPE_BAN for bans (+b) and EXBTYPE_EXCEPT for excepts (+e)

The is_ok function is called in a number of cases:

  • If b->is_ok_check is EXBCHK_ACCESS then the function is called to check if the user may set/unset the mode. You must only check access and NOT send any error message(s). If access is granted you should return EX_ALLOW. If you want to disallow a set/unset then return EX_DENY. Normally IRC Operators with override capabilities may still override a deny, if you also want to prevent IRCOp's from (un)setting then you can return EX_ALWAYS_DENY.
  • When b->is_ok_check is EXCHK_ACCESS_ERR it means your function is called (again) to check access but this time you may send an error message to the user (in fact, you are expected to), indicating why setting or unsetting is not permitted.
  • Finally, when b->is_ok_check is EXCHK_PARAM then you are supposed to check the correctness of the parameter being passed. If it's not good (eg: you expect ~example:num where num is a value between 1 and 20 and you recieve 'lalala' or '30') then you return EX_DENY. Otherwise you return EX_ALLOW. You should also send an error message to the user telling them what's wrong.

Example: TODO

is_banned

Check if a user is affected by this ban.

Declaration:

int is_banned(BanContext *b)
Variable Type Description
param BanContext * The ban context

In the BanContext *b, the following values are useful:

Variable Type Description
b->client Client * Client that is trying to join/message/etc
b->channel Channel * Channel in which the event is happening (can be NULL for TKL events, if you registered your extban with BANCHK_TKL)
b->banstr const char * The ban string (eg: "something" for "/MODE #chan +b ~example:something")
b->is_ok_check ExtbanCheck Check type, one of EXBCHK_*. Explained later.
b->what int Used to differentiate between adding and removing the mode, one of: MODE_ADD or MODE_DEL
b->ban_type ExtbanType EXBTYPE_BAN for bans (+b) and EXBTYPE_EXCEPT for excepts (+e)
msg const char ** The channel message (if used in PRIVMSG/NOTICE/.., otherwise this is NULL)
errmsg const char ** An optional (different) error message to be returned if the user is banned

Return: simply return 1 if the user is banned and 0 otherwise.

The b->ban_check_types decides what kind of check you need to do:

Ban check type Description
BANCHK_JOIN User is trying to join the channel
BANCHK_MSG User is trying to send a message or notice to the channel
BANCHK_NICK User is trying to change his/her nick
BANCHK_LEAVE_MSG User is leaving via PART or QUIT - allows you censoring of the message (the part or quit is NOT blocked, only the part reason / quit reason)
BANCHK_TKL Called from a server ban routine (eg GLINE) or other match_user() routine.
Careful! b->channel will be NULL and the user may not be online yet so b->client->user and other fields could be NULL too

Example code:

/* Dull example of a ~example:blah ban type where 'blah' must match the exact nick name of the user. No wildcards. */
int mymod_is_banned(BanContext *b)
{
    if (!stricmp(b->banstr, b->client->name))
        return 1;
    return 0;
}

More information

UnrealIRCd ships with quite a lot of extended ban modules that you can use for examples and inspiration, they are all in src/modules/extbans/