unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
unreal_server_compat.c (8112B)
1 /* 2 * unreal_server_compat - Compatibility with pre-U6 servers 3 * (C) Copyright 2016-2021 Bram Matthys (Syzop) 4 * License: GPLv2 or later 5 * 6 * Currently the only purpose of this module is to rewrite MODE 7 * and SJOIN lines to older servers so any bans/exempts/invex 8 * will show up with their single letter syntax, 9 * eg "MODE #test +b ~account:someacc" will be rewritten 10 * as "MODE #test +b ~a:someacc". 11 * It uses rather complex mode reparsing techniques to 12 * achieve this, but this was deemed to be the only way 13 * that we could achieve this in a doable way. 14 * The alternative was complicating the mode.c code with 15 * creating multiple strings for multiple clients, and 16 * doing the same in any other MODE change routine. 17 * That would have caused rather intrussive compatibility 18 * code, so I don't want that. 19 * With this we can just rip out the module at some point 20 * that we no longer want to support pre-U6 protocol. 21 * For SJOIN we do something similar, though in that case 22 * it would have been quite doable to handle it in there. 23 * Just figured I would stuff it in here as well, since 24 * it is basically the same case. 25 * -- Syzop 26 */ 27 28 #include "unrealircd.h" 29 30 ModuleHeader MOD_HEADER 31 = { 32 "unreal_server_compat", 33 "1.0.0", 34 "Provides compatibility with non-U6 servers", 35 "Bram Matthys (Syzop)", 36 "unrealircd-6" 37 }; 38 39 /* Forward declarations */ 40 int usc_packet(Client *from, Client *to, Client *intended_to, char **msg, int *length); 41 int usc_reparse_mode(char **msg, char *p, int *length); 42 int usc_reparse_sjoin(char **msg, char *p, int *length); 43 void skip_spaces(char **p); 44 void read_until_space(char **p); 45 int eat_parameter(char **p); 46 47 MOD_INIT() 48 { 49 MARK_AS_OFFICIAL_MODULE(modinfo); 50 HookAdd(modinfo->handle, HOOKTYPE_PACKET, 0, usc_packet); 51 return MOD_SUCCESS; 52 } 53 54 MOD_LOAD() 55 { 56 return MOD_SUCCESS; 57 } 58 59 60 MOD_UNLOAD() 61 { 62 return MOD_SUCCESS; 63 } 64 65 int usc_packet(Client *from, Client *to, Client *intended_to, char **msg, int *length) 66 { 67 char *p, *buf = *msg; 68 69 /* We are only interested in outgoing servers 70 * that do not support PROTOCTL NEXTBANS 71 */ 72 if (IsMe(to) || !IsServer(to) || SupportNEXTBANS(to) || !buf || !length || !*length) 73 return 0; 74 75 buf[*length] = '\0'; /* safety */ 76 77 p = *msg; 78 79 skip_spaces(&p); 80 /* Skip over message tags */ 81 if (*p == '@') 82 { 83 read_until_space(&p); 84 if (*p == '\0') 85 return 0; /* unexpected ending */ 86 p++; 87 } 88 89 skip_spaces(&p); 90 if (*p == '\0') 91 return 0; 92 93 /* Skip origin */ 94 if (*p == ':') 95 { 96 read_until_space(&p); 97 if (*p == '\0') 98 return 0; /* unexpected ending */ 99 } 100 101 skip_spaces(&p); 102 if (*p == '\0') 103 return 0; 104 105 if (!strncmp(p, "MODE ", 5)) /* MODE #channel */ 106 { 107 if (!eat_parameter(&p)) 108 return 0; 109 /* p now points to #channel */ 110 111 /* Now it gets interesting... we have to re-parse and re-write the entire MODE line. */ 112 return usc_reparse_mode(msg, p, length); 113 } 114 115 if (!strncmp(p, "SJOIN ", 6)) /* SJOIN timestamp #channel */ 116 { 117 if (!eat_parameter(&p) || !eat_parameter(&p)) 118 return 0; 119 /* p now points to #channel */ 120 121 /* Now it gets interesting... we have to re-parse and re-write the entire SJOIN line. */ 122 return usc_reparse_sjoin(msg, p, length); 123 } 124 125 return 0; 126 } 127 128 int usc_reparse_mode(char **msg, char *p, int *length) 129 { 130 static char obuf[8192]; 131 char modebuf[512], *mode_buf_p, *para_buf_p; 132 char *channel_name; 133 int i; 134 int n; 135 ParseMode pm; 136 int modes_processed = 0; 137 138 channel_name = p; 139 if (!eat_parameter(&p)) 140 return 0; 141 142 mode_buf_p = p; 143 if (!eat_parameter(&p)) 144 return 0; 145 strlncpy(modebuf, mode_buf_p, sizeof(modebuf), p - mode_buf_p); 146 147 /* If we get here then it is (for example) a 148 * MODE #channel +b nick!user@host 149 * So, has at least one parameter (nick!user@host in the example). 150 * p now points exactly to the 'n' from nick!user@host. 151 * 152 * Now, what we will do: 153 * everything BEFORE p is the 'header' that we will 154 * send exactly as-is. 155 * The only thing we may (potentially) change is 156 * everything AFTER p! 157 */ 158 159 /* Fill 'obuf' with that 'header' */ 160 strlncpy(obuf, *msg, sizeof(obuf), p - *msg); 161 para_buf_p = p; 162 163 /* Now parse the modes */ 164 for (n = parse_chanmode(&pm, modebuf, para_buf_p); n; n = parse_chanmode(&pm, NULL, NULL)) 165 { 166 /* We only rewrite the parameters, so don't care about paramless modes.. */ 167 if (!pm.param) 168 continue; 169 170 if ((pm.modechar == 'b') || (pm.modechar == 'e') || (pm.modechar == 'I')) 171 { 172 const char *result = clean_ban_mask(pm.param, pm.what, &me, 1); 173 strlcat(obuf, result?result:"<invalid>", sizeof(obuf)); 174 strlcat(obuf, " ", sizeof(obuf)); 175 } else 176 { 177 /* as-is */ 178 strlcat(obuf, pm.param, sizeof(obuf)); 179 strlcat(obuf, " ", sizeof(obuf)); 180 } 181 modes_processed++; 182 } 183 184 /* Send line as-is */ 185 if (modes_processed == 0) 186 return 0; 187 188 /* Strip final whitespace */ 189 if (obuf[strlen(obuf)-1] == ' ') 190 obuf[strlen(obuf)-1] = '\0'; 191 192 if (pm.parabuf && *pm.parabuf) 193 { 194 strlcat(obuf, " ", sizeof(obuf)); 195 strlcat(obuf, pm.parabuf, sizeof(obuf)); 196 } 197 198 /* Add CRLF */ 199 if (obuf[strlen(obuf)-1] != '\n') 200 strlcat(obuf, "\r\n", sizeof(obuf)); 201 202 /* Line modified, use it! */ 203 *msg = obuf; 204 *length = strlen(obuf); 205 206 return 0; 207 } 208 209 int usc_reparse_sjoin(char **msg, char *p, int *length) 210 { 211 static char obuf[8192]; 212 char parabuf[512]; 213 char *save = NULL; 214 char *s; 215 216 /* Skip right to the last parameter, the only one we care about */ 217 p = strstr(p, " :"); 218 if (!p) 219 return 0; 220 p += 2; 221 222 /* Save everything before p, put it in obuf... */ 223 224 /* Fill 'obuf' with that 'header' */ 225 strlncpy(obuf, *msg, sizeof(obuf), p - *msg); 226 227 /* Put parameters in parabuf so we can trash it :D */ 228 strlcpy(parabuf, p, sizeof(parabuf)); 229 230 /* Now parse the SJOIN */ 231 for (s = strtoken(&save, parabuf, " "); s; s = strtoken(&save, NULL, " ")) 232 { 233 if (*s == '<') 234 { 235 /* SJSBY */ 236 char *next = strchr(s, '>'); 237 const char *result; 238 if (!next) 239 { 240 unreal_log(ULOG_WARNING, "unreal_server_compat", "USC_REPARSE_SJOIN_FAILURE", NULL, 241 "[unreal_server_compat] usc_reparse_sjoin(): sjoin data '$ban' seemed like a SJSBY but was not??", 242 log_data_string("ban", s)); 243 continue; 244 } 245 if (!strchr("&\"\\", next[1])) 246 goto fallback_usc_reparse_sjoin; 247 *next++ = '\0'; 248 result = clean_ban_mask(next+1, MODE_ADD, &me, 1); 249 if (!result) 250 { 251 unreal_log(ULOG_WARNING, "unreal_server_compat", "USC_REPARSE_SJOIN_FAILURE", NULL, 252 "[unreal_server_compat] usc_reparse_sjoin(): ban '$ban' could not be converted", 253 log_data_string("ban", s+1)); 254 continue; 255 } 256 strlcat(obuf, s, sizeof(obuf)); /* "<123,nick" */ 257 strlcat(obuf, ">", sizeof(obuf)); /* > */ 258 strlncat(obuf, next, sizeof(obuf), 1); /* & or \" or \\ */ 259 strlcat(obuf, result, sizeof(obuf)); /* the converted result */ 260 strlcat(obuf, " ", sizeof(obuf)); 261 } else 262 if (strchr("&\"\\", *s)) 263 { 264 /* +b / +e / +I */ 265 const char *result = clean_ban_mask(s+1, MODE_ADD, &me, 1); 266 if (!result) 267 { 268 unreal_log(ULOG_WARNING, "unreal_server_compat", "USC_REPARSE_SJOIN_FAILURE", NULL, 269 "[unreal_server_compat] usc_reparse_sjoin(): ban '$ban' could not be converted", 270 log_data_string("ban", s+1)); 271 continue; 272 } 273 strlncat(obuf, s, sizeof(obuf), 1); 274 strlcat(obuf, result, sizeof(obuf)); 275 strlcat(obuf, " ", sizeof(obuf)); 276 } else { 277 fallback_usc_reparse_sjoin: 278 strlcat(obuf, s, sizeof(obuf)); 279 strlcat(obuf, " ", sizeof(obuf)); 280 } 281 } 282 283 /* Strip final whitespace */ 284 if (obuf[strlen(obuf)-1] == ' ') 285 obuf[strlen(obuf)-1] = '\0'; 286 287 /* Add CRLF */ 288 if (obuf[strlen(obuf)-1] != '\n') 289 strlcat(obuf, "\r\n", sizeof(obuf)); 290 291 /* And use it! */ 292 *msg = obuf; 293 *length = strlen(obuf); 294 295 return 0; 296 } 297 298 /** Skip space(s), if any. */ 299 void skip_spaces(char **p) 300 { 301 for (; **p == ' '; *p = *p + 1); 302 } 303 304 /** Keep reading until we hit space. */ 305 void read_until_space(char **p) 306 { 307 for (; **p && (**p != ' '); *p = *p + 1); 308 } 309 310 int eat_parameter(char **p) 311 { 312 read_until_space(p); 313 if (**p == '\0') 314 return 0; /* was just a "MODE #channel" query - wait.. that's weird we are a server sending this :D */ 315 skip_spaces(p); 316 if (**p == '\0') 317 return 0; // impossible 318 return 1; 319 }