diff --git a/doc/ircd.conf.example b/doc/ircd.conf.example index 79fecbe6..cdca5c70 100755 --- a/doc/ircd.conf.example +++ b/doc/ircd.conf.example @@ -451,6 +451,14 @@ alias "MS" { target = "MemoServ"; }; +/* +fakechannel "#honeypot" { + topic = "Come in"; + users_min = 50; + users_max = 300; +}; +*/ + general { hide_error_messages = opers; hide_spoof_ips = yes; @@ -520,6 +528,7 @@ general { caller_id_wait = 1 minute; pace_wait_simple = 1 second; pace_wait = 10 seconds; + listfake_wait = 180 seconds; short_motd = no; ping_cookie = no; connect_timeout = 30 seconds; diff --git a/doc/reference.conf b/doc/reference.conf index 91d18b02..35d1096a 100755 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -927,6 +927,14 @@ alias "MS" { target = "MemoServ"; }; +/* +fakechannel "#honeypot" { + topic = "Come in"; + users_min = 50; + users_max = 300; +}; +*/ + /* The general block contains many of the options that were once compiled * in options in config.h. The general block is read at start time. */ @@ -1181,6 +1189,9 @@ general { */ pace_wait = 10 seconds; + /* listfake_wait: time until real list command can be used */ + listfake_wait = 180 seconds; + /* short motd: send clients a notice telling them to read the motd * instead of forcing a motd to clients who may simply ignore it. */ diff --git a/include/parse.h b/include/parse.h index 921bfe6f..802090db 100644 --- a/include/parse.h +++ b/include/parse.h @@ -41,5 +41,6 @@ extern void mod_del_cmd(struct Message *msg); extern void report_messages(struct Client *); extern struct Dictionary *alias_dict; +extern struct Dictionary *fakechannel_dict; #endif /* INCLUDED_parse_h_h */ diff --git a/include/s_conf.h b/include/s_conf.h index 42e26705..15af0a64 100644 --- a/include/s_conf.h +++ b/include/s_conf.h @@ -191,6 +191,7 @@ struct config_file_entry int operspy_admin_only; int pace_wait; int pace_wait_simple; + int listfake_wait; int short_motd; int no_oper_flood; int hide_server; @@ -309,6 +310,16 @@ struct alias_entry int hits; }; +struct fakechannel_entry +{ + char *name; + char *topic; + + int users_min; + int users_max; +}; + + /* All variables are GLOBAL */ extern int specific_ipv4_vhost; /* used in s_bsd.c */ extern int specific_ipv6_vhost; diff --git a/modules/m_info.c b/modules/m_info.c index 301b9645..7a98b8a2 100644 --- a/modules/m_info.c +++ b/modules/m_info.c @@ -398,6 +398,12 @@ static struct InfoStruct info_table[] = { &ConfigFileEntry.pace_wait_simple, "Minimum delay between less intensive commands" }, + { + "listfake_wait", + OUTPUT_DECIMAL, + &ConfigFileEntry.listfake_wait, + "Time until real list command can be used" + }, { "ping_cookie", OUTPUT_BOOLEAN, diff --git a/modules/m_list.c b/modules/m_list.c index dff4ed8f..9cca0a01 100644 --- a/modules/m_list.c +++ b/modules/m_list.c @@ -153,6 +153,32 @@ static int m_list(struct Client *client_p, struct Client *source_p, int parc, co last_used = rb_current_time(); } + /* Disable LIST for a configured timespan after connect and send configured fake + * channels instead. + * Exempts: Opers, identifed users and users with spambot_exempt flag + */ + if (((source_p->localClient->firsttime + ConfigFileEntry.listfake_wait) > rb_current_time()) + && !IsOper(source_p) && !IsExemptSpambot(source_p) && + !(source_p->user != NULL && !EmptyString(source_p->user->suser))) + { + struct fakechannel_entry *fakechannel; + struct DictionaryIter iter; + + sendto_one(source_p, form_str(RPL_LISTSTART), me.name, source_p->name); + + DICTIONARY_FOREACH(fakechannel, &iter, fakechannel_dict) + { + sendto_one(source_p, form_str(RPL_LIST), me.name, source_p->name, + "", + fakechannel->name, + (rand() % fakechannel->users_max + fakechannel->users_min), + fakechannel->topic); + } + + sendto_one(source_p, form_str(RPL_LISTEND), me.name, source_p->name); + return 0; + } + return mo_list(client_p, source_p, parc, parv); } diff --git a/src/newconf.c b/src/newconf.c index 9edcb262..4ae28d65 100644 --- a/src/newconf.c +++ b/src/newconf.c @@ -54,6 +54,7 @@ static rb_dlink_list yy_cluster_list; static struct oper_conf *yy_oper = NULL; static struct alias_entry *yy_alias = NULL; +static struct fakechannel_entry *yy_fakechannel = NULL; static char *yy_blacklist_host = NULL; static char *yy_blacklist_reason = NULL; @@ -1802,6 +1803,92 @@ conf_set_alias_target(void *data) yy_alias->target = rb_strdup(data); } +static int +conf_begin_fakechannel(struct TopConf *tc) +{ + yy_fakechannel = rb_malloc(sizeof(struct fakechannel_entry)); + + if (conf_cur_block_name != NULL) + yy_fakechannel->name = rb_strdup(conf_cur_block_name); + + /* Set defaults */ + yy_fakechannel->topic = ""; + yy_fakechannel->users_min = 50; + yy_fakechannel->users_max = 300; + + return 0; +} + +static int +conf_end_fakechannel(struct TopConf *tc) +{ + if (yy_fakechannel == NULL) + return -1; + + if (yy_fakechannel->name == NULL) + { + conf_report_error("Ignoring fakechannel -- must have a name."); + + rb_free(yy_fakechannel); + + return -1; + } + + irc_dictionary_add(fakechannel_dict, yy_fakechannel->name, yy_fakechannel); + + return 0; +} + +static void +conf_set_fakechannel_name(void *data) +{ + if (data == NULL || yy_fakechannel == NULL) /* this shouldn't ever happen */ + return; + + yy_fakechannel->name = rb_strdup(data); +} + +static void +conf_set_fakechannel_topic(void *data) +{ + if (data == NULL || yy_fakechannel == NULL) /* this shouldn't ever happen */ + return; + + yy_fakechannel->topic = rb_strdup(data); +} + +static void +conf_set_fakechannel_users_min(void *data) +{ + if (data == NULL || yy_fakechannel == NULL) /* this shouldn't ever happen */ + return; + + int users_min = *((int *)data); + if(users_min <= 0) + { + conf_report_error("fakechannel::users_min value %d is bogus, ignoring", users_min); + return; + } + + yy_fakechannel->users_min = users_min; +} + +static void +conf_set_fakechannel_users_max(void *data) +{ + if (data == NULL || yy_fakechannel == NULL) /* this shouldn't ever happen */ + return; + + int users_max = *((int *)data); + if(users_max <= 0) + { + conf_report_error("fakechannel::users_max value %d is bogus, ignoring", users_max); + return; + } + + yy_fakechannel->users_max = users_max; +} + static void conf_set_channel_autochanmodes(void *data) { @@ -2410,6 +2497,7 @@ static struct ConfEntry conf_general_table[] = { "operspy_dont_care_user_info", CF_YESNO, NULL, 0, &ConfigFileEntry.operspy_dont_care_user_info }, { "pace_wait", CF_TIME, NULL, 0, &ConfigFileEntry.pace_wait }, { "pace_wait_simple", CF_TIME, NULL, 0, &ConfigFileEntry.pace_wait_simple }, + { "listfake_wait", CF_TIME, NULL, 0, &ConfigFileEntry.listfake_wait }, { "ping_cookie", CF_YESNO, NULL, 0, &ConfigFileEntry.ping_cookie }, { "reject_after_count", CF_INT, NULL, 0, &ConfigFileEntry.reject_after_count }, { "reject_ban_time", CF_TIME, NULL, 0, &ConfigFileEntry.reject_ban_time }, @@ -2523,6 +2611,12 @@ newconf_init() add_conf_item("alias", "name", CF_QSTRING, conf_set_alias_name); add_conf_item("alias", "target", CF_QSTRING, conf_set_alias_target); + add_top_conf("fakechannel", conf_begin_fakechannel, conf_end_fakechannel, NULL); + add_conf_item("fakechannel", "name", CF_QSTRING, conf_set_fakechannel_name); + add_conf_item("fakechannel", "topic", CF_QSTRING, conf_set_fakechannel_topic); + add_conf_item("fakechannel", "users_max", CF_INT, conf_set_fakechannel_users_max); + add_conf_item("fakechannel", "users_min", CF_INT, conf_set_fakechannel_users_min); + add_top_conf("blacklist", NULL, NULL, NULL); add_conf_item("blacklist", "host", CF_QSTRING, conf_set_blacklist_host); add_conf_item("blacklist", "type", CF_STRING | CF_FLIST, conf_set_blacklist_type); diff --git a/src/parse.c b/src/parse.c index 190c9f08..3ea43474 100644 --- a/src/parse.c +++ b/src/parse.c @@ -45,6 +45,7 @@ static struct Dictionary *cmd_dict = NULL; struct Dictionary *alias_dict = NULL; +struct Dictionary *fakechannel_dict = NULL; /* parv[0] is not used, and parv[LAST] == NULL */ static char *para[MAXPARA + 2]; diff --git a/src/s_conf.c b/src/s_conf.c index 1141b818..4f6eb823 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -738,6 +738,7 @@ set_default_conf(void) ConfigFileEntry.pace_wait = 10; ConfigFileEntry.caller_id_wait = 60; ConfigFileEntry.pace_wait_simple = 1; + ConfigFileEntry.listfake_wait = 180; ConfigFileEntry.short_motd = NO; ConfigFileEntry.no_oper_flood = NO; ConfigFileEntry.fname_userlog = NULL; @@ -826,6 +827,9 @@ set_default_conf(void) if (!alias_dict) alias_dict = irc_dictionary_create(strcasecmp); + + if (!fakechannel_dict) + fakechannel_dict = irc_dictionary_create(strcasecmp); } #undef YES @@ -1434,6 +1438,19 @@ free_alias_cb(struct DictionaryElement *ptr, void *unused) rb_free(aptr); } +/* + * free an fakechannel{} entry. + */ +static void +free_fakechannel_cb(struct DictionaryElement *ptr, void *unused) +{ + struct fakechannel_entry *aptr = ptr->data; + + rb_free(aptr->name); + rb_free(aptr->topic); + rb_free(aptr); +} + /* * clear_out_old_conf * @@ -1537,6 +1554,13 @@ clear_out_old_conf(void) alias_dict = NULL; } + /* remove any fakechannels */ + if (alias_dict != NULL) + { + irc_dictionary_destroy(fakechannel_dict, free_fakechannel_cb, NULL); + fakechannel_dict = NULL; + } + destroy_blacklists(); privilegeset_mark_all_illegal();