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
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 useBANCHK_ALL
here, but if you only want the ban to apply on for example joining then you set it toBANCHK_JOIN
. You can also setBANCHK_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/