/*************************************************************************** * Mud Telopt Handler 1.3 by Igor van den Hoven. 06 Apr 2009 * ***************************************************************************/ #include <string.h> #include <stdarg.h> #include "mud.h" #include "telnet.h" /* Externals */ bool write_to_descriptor( DESCRIPTOR_DATA *d, const char *txt, int length ); size_t skill_package_table_size( void ); extern int top_area; extern int top_help; extern int top_mob_index; extern int top_obj_index; extern int top_room; #define TELOPT_DEBUG 1 #define ANNOUNCE_WILL BV01 #define ANNOUNCE_DO BV02 const char iac_will_ttype[] = { IAC, WILL, TELOPT_TTYPE, 0 }; const char iac_sb_ttype_is[] = { IAC, SB, TELOPT_TTYPE, ENV_IS, 0 }; const char iac_sb_naws[] = { IAC, SB, TELOPT_NAWS, 0 }; const char iac_will_new_environ[] = { IAC, WILL, TELOPT_NEW_ENVIRON, 0 }; const char iac_sb_new_environ[] = { IAC, SB, TELOPT_NEW_ENVIRON, ENV_IS,0 }; const char iac_do_mssp[] = { IAC, DO, TELOPT_MSSP, 0 }; const char iac_do_mccp[] = { IAC, DO, TELOPT_MCCP, 0 }; const char iac_dont_mccp[] = { IAC, DONT, TELOPT_MCCP, 0 }; /* Exported functions */ int translate_telopts( DESCRIPTOR_DATA *d, char *src, int srclen, char *out ); void announce_support( DESCRIPTOR_DATA *d ); int start_compress( DESCRIPTOR_DATA *d ); void end_compress( DESCRIPTOR_DATA *d ); void write_compressed( DESCRIPTOR_DATA *d ); void send_echo_on( DESCRIPTOR_DATA *d ); void send_echo_off( DESCRIPTOR_DATA *d ); /* Private functions */ static void debug_telopts( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ); static int process_will_ttype( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ); static int process_sb_ttype_is( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ); static int process_sb_naws( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ); static int process_will_new_environ( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ); static int process_sb_new_environ( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ); static int process_do_mssp( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ); static int process_do_mccp( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ); static int process_dont_mccp( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ); static int skip_sb( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ); static void descriptor_printf( DESCRIPTOR_DATA *d, const char *fmt, ...); static void process_compressed( DESCRIPTOR_DATA *d ); struct telopt_type { int size; const char * code; int (* func) (DESCRIPTOR_DATA *d, unsigned char *src, int srclen); }; const struct telopt_type telopt_table [] = { { 3, iac_will_ttype, &process_will_ttype}, { 4, iac_sb_ttype_is, &process_sb_ttype_is}, { 3, iac_sb_naws, &process_sb_naws}, { 3, iac_will_new_environ, &process_will_new_environ}, { 4, iac_sb_new_environ, &process_sb_new_environ}, { 3, iac_do_mssp, &process_do_mssp}, { 3, iac_do_mccp, &process_do_mccp}, { 3, iac_dont_mccp, &process_dont_mccp}, { 0, NULL, NULL} }; const char *telcmds[] = { "EOF", "SUSP", "ABORT", "EOR", "SE", "NOP", "DMARK", "BRK", "IP", "AO", "AYT", "EC", "EL", "GA", "SB", "WILL", "WONT", "DO", "DONT", "IAC" }; struct telnet_type { const char *name; int flags; }; struct telnet_type telnet_table[] = { { "BINARY", 0 }, { "ECHO", 0 }, /* Local echo */ { "RCP", 0 }, { "SUPPRESS GA", 0 }, /* Character mode */ { "NAME", 0 }, { "STATUS", 0 }, { "TIMING MARK", 0 }, { "RCTE", 0 }, { "NAOL", 0 }, { "NAOP", 0 }, { "NAORCD", 0 }, { "NAOHTS", 0 }, { "NAOHTD", 0 }, { "NAOFFD", 0 }, { "NAOVTS", 0 }, { "NAOVTD", 0 }, { "NAOLFD", 0 }, { "EXTEND ASCII", 0 }, { "LOGOUT", 0 }, { "BYTE MACRO", 0 }, { "DATA ENTRY TERML", 0 }, { "SUPDUP", 0 }, { "SUPDUP OUTPUT", 0 }, { "SEND LOCATION", 0 }, { "TERMINAL TYPE", ANNOUNCE_DO }, /* Terminal Type */ { "EOR", 0 }, /* End of Record */ { "TACACS UID", 0 }, { "OUTPUT MARKING", 0 }, { "TTYLOC", 0 }, { "3270 REGIME", 0 }, { "X.3 PAD", 0 }, { "NAWS", ANNOUNCE_DO }, /* Negotiate About Window Size */ { "TSPEED", 0 }, { "LFLOW", 0 }, { "LINEMODE", 0 }, { "XDISPLOC", 0 }, { "OLD_ENVIRON", 0 }, { "AUTH", 0 }, { "ENCRYPT", 0 }, { "NEW_ENVIRON", ANNOUNCE_DO }, /* Used to detect Win32 telnet */ { "TN3270E", 0 }, { "XAUTH", 0 }, { "CHARSET", 0 }, { "RSP", 0 }, { "COM PORT", 0 }, { "SLE", 0 }, { "STARTTLS", 0 }, { "KERMIT", 0 }, { "SEND-URL", 0 }, { "FORWARD_X", 0 }, { "50", 0 }, { "51", 0 }, { "52", 0 }, { "53", 0 }, { "54", 0 }, { "55", 0 }, { "56", 0 }, { "57", 0 }, { "58", 0 }, { "59", 0 }, { "60", 0 }, { "61", 0 }, { "62", 0 }, { "63", 0 }, { "64", 0 }, { "65", 0 }, { "66", 0 }, { "67", 0 }, { "68", 0 }, { "69", 0 }, { "MSSP", ANNOUNCE_WILL }, { "71", 0 }, { "72", 0 }, { "73", 0 }, { "74", 0 }, { "75", 0 }, { "76", 0 }, { "77", 0 }, { "78", 0 }, { "79", 0 }, { "80", 0 }, { "81", 0 }, { "82", 0 }, { "83", 0 }, { "84", 0 }, { "MCCP1", 0 }, /* Obsolete - don't use. */ { "MCCP2", ANNOUNCE_WILL }, /* Mud Client Compression Protocol */ { "87", 0 }, { "88", 0 }, { "89", 0 }, { "MSP", 0 }, { "MXP", 0 }, { "MSP2", 0 }, /* Unadopted */ { "ZMP", 0 }, /* Unadopted */ { "94", 0 }, { "95", 0 }, { "96", 0 }, { "97", 0 }, { "98", 0 }, { "99", 0 }, { "100", 0 }, { "101", 0 }, { "102", 0 }, /* Aardwolf */ { "103", 0 }, { "104", 0 }, { "105", 0 }, { "106", 0 }, { "107", 0 }, { "108", 0 }, { "109", 0 }, { "110", 0 }, { "111", 0 }, { "112", 0 }, { "113", 0 }, { "114", 0 }, { "115", 0 }, { "116", 0 }, { "117", 0 }, { "118", 0 }, { "119", 0 }, { "120", 0 }, { "121", 0 }, { "122", 0 }, { "123", 0 }, { "124", 0 }, { "125", 0 }, { "126", 0 }, { "127", 0 }, { "128", 0 }, { "129", 0 }, { "130", 0 }, { "131", 0 }, { "132", 0 }, { "133", 0 }, { "134", 0 }, { "135", 0 }, { "136", 0 }, { "137", 0 }, { "138", 0 }, { "139", 0 }, { "140", 0 }, { "141", 0 }, { "142", 0 }, { "143", 0 }, { "144", 0 }, { "145", 0 }, { "146", 0 }, { "147", 0 }, { "148", 0 }, { "149", 0 }, { "150", 0 }, { "151", 0 }, { "152", 0 }, { "153", 0 }, { "154", 0 }, { "155", 0 }, { "156", 0 }, { "157", 0 }, { "158", 0 }, { "159", 0 }, { "160", 0 }, { "161", 0 }, { "162", 0 }, { "163", 0 }, { "164", 0 }, { "165", 0 }, { "166", 0 }, { "167", 0 }, { "168", 0 }, { "169", 0 }, { "170", 0 }, { "171", 0 }, { "172", 0 }, { "173", 0 }, { "174", 0 }, { "175", 0 }, { "176", 0 }, { "177", 0 }, { "178", 0 }, { "179", 0 }, { "180", 0 }, { "181", 0 }, { "182", 0 }, { "183", 0 }, { "184", 0 }, { "185", 0 }, { "186", 0 }, { "187", 0 }, { "188", 0 }, { "189", 0 }, { "190", 0 }, { "191", 0 }, { "192", 0 }, { "193", 0 }, { "194", 0 }, { "195", 0 }, { "196", 0 }, { "197", 0 }, { "198", 0 }, { "199", 0 }, { "200", 0 }, /* Achaea */ { "201", 0 }, { "202", 0 }, { "203", 0 }, { "204", 0 }, { "205", 0 }, { "206", 0 }, { "207", 0 }, { "208", 0 }, { "209", 0 }, { "210", 0 }, { "211", 0 }, { "212", 0 }, { "213", 0 }, { "214", 0 }, { "215", 0 }, { "216", 0 }, { "217", 0 }, { "218", 0 }, { "219", 0 }, { "220", 0 }, { "221", 0 }, { "222", 0 }, { "223", 0 }, { "224", 0 }, { "225", 0 }, { "226", 0 }, { "227", 0 }, { "228", 0 }, { "229", 0 }, { "230", 0 }, { "231", 0 }, { "232", 0 }, { "233", 0 }, { "234", 0 }, { "235", 0 }, { "236", 0 }, { "237", 0 }, { "238", 0 }, { "239", 0 }, { "240", 0 }, { "241", 0 }, { "242", 0 }, { "243", 0 }, { "244", 0 }, { "245", 0 }, { "246", 0 }, { "247", 0 }, { "248", 0 }, { "249", 0 }, { "250", 0 }, { "251", 0 }, { "252", 0 }, { "253", 0 }, { "254", 0 }, { "255", 0 } }; /* Call this to announce support for telopts marked as such in tables.c */ void announce_support( DESCRIPTOR_DATA *d) { int i = 0; for (i = 0 ; i < 255 ; i++) { if (telnet_table[i].flags) { if (IS_SET(telnet_table[i].flags, ANNOUNCE_WILL)) { descriptor_printf(d, "%c%c%c", IAC, WILL, i); } if (IS_SET(telnet_table[i].flags, ANNOUNCE_DO)) { descriptor_printf(d, "%c%c%c", IAC, DO, i); } } } } /* This is the main routine that strips out and handles telopt negotiations. It also deals with \r and \0 so commands are separated by a single \n. */ int translate_telopts(DESCRIPTOR_DATA *d, char *src, int srclen, char *out) { int cnt = 0, skip = 0; unsigned char *pti = NULL, *pto = NULL; pti = (unsigned char*) src; pto = (unsigned char*) out; if (d->teltop) { if (d->teltop + srclen + 1 < MAX_INPUT_LENGTH) { memcpy(d->telbuf + d->teltop, src, srclen); srclen += d->teltop; pti = (unsigned char*) d->telbuf; } d->teltop = 0; } while (srclen > 0) { switch (*pti) { case IAC: skip = 2; debug_telopts(d, pti, srclen); for (cnt = 0 ; telopt_table[cnt].code ; cnt++) { if (srclen < telopt_table[cnt].size) { if (!memcmp(pti, telopt_table[cnt].code, srclen)) { skip = telopt_table[cnt].size; break; } } else { if (!memcmp(pti, telopt_table[cnt].code, telopt_table[cnt].size)) { skip = telopt_table[cnt].func(d, pti, srclen); break; } } } if (telopt_table[cnt].code == NULL && srclen > 1) { switch (pti[1]) { case WILL: case DO: case WONT: case DONT: skip = 3; break; case SB: skip = skip_sb(d, pti, srclen); break; case IAC: *pto++ = *pti++; srclen--; skip = 1; break; default: if (TELCMD_OK(pti[1])) { skip = 2; } else { skip = 1; } break; } } if (skip <= srclen) { pti += skip; srclen -= skip; } else { memcpy(d->telbuf, pti, srclen); d->teltop = srclen; *pto = 0; return strlen(out); } break; case '\r': if (srclen > 1 && pti[1] == '\0') { *pto++ = '\n'; } pti++; srclen--; break; case '\0': pti++; srclen--; break; default: *pto++ = *pti++; srclen--; break; } } *pto = 0; return strlen(out); } void debug_telopts( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ) { #if SWR2_DEBUG_TELNET == 1 if (srclen > 1 && TELOPT_DEBUG) { switch(src[1]) { case IAC: log_printf("D%d@%s RCVD IAC IAC", d->descriptor, d->host); break; case DO: case DONT: case WILL: case WONT: case SB: if (srclen > 2) { log_printf("D%d@%s RCVD IAC %s %s", d->descriptor, d->host, TELCMD(src[1]), TELOPT(src[2])); } else { log_printf("D%d@%s RCVD IAC %s ?", d->descriptor, d->host, TELCMD(src[1])); } break; default: if (TELCMD_OK(src[1])) { log_printf("D%d@%s RCVD IAC %s", d->descriptor, d->host, TELCMD(src[1])); } else { log_printf("D%d@%s RCVD IAC %d", d->descriptor, d->host, src[1]); } break; } } else { log_printf("D%d@%s RCVD IAC ?", d->descriptor, d->host); } #endif } /* Send to client to have it disable local echo */ void send_echo_off( DESCRIPTOR_DATA *d ) { descriptor_printf(d, "%c%c%c", IAC, WILL, TELOPT_ECHO); } /* Send to client to have it enable local echo */ void send_echo_on( DESCRIPTOR_DATA *d ) { descriptor_printf(d, "%c%c%c", IAC, WONT, TELOPT_ECHO); } /* Terminal Type negotiation - make sure d->terminal_type is initialized. */ int process_will_ttype( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ) { if (*d->terminal_type == 0) { descriptor_printf(d, "%c%c%c%c%c%c", IAC, SB, TELOPT_TTYPE, ENV_SEND, IAC, SE); } return 3; } int process_sb_ttype_is( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ) { char val[MAX_INPUT_LENGTH]; char *pto = val; int i = 0; if (skip_sb(d, src, srclen) > srclen) { return srclen + 1; } for (i = 4 ; i < srclen && src[i] != SE ; i++) { switch (src[i]) { default: *pto++ = src[i]; break; case IAC: *pto = 0; STRFREE( d->terminal_type ); d->terminal_type = STRALLOC( val ); break; } } return i + 1; } /* NAWS: Negotiate About Window Size */ int process_sb_naws( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ) { int i, j; d->cols = d->rows = 0; if (skip_sb(d, src, srclen) > srclen) { return srclen + 1; } for (i = 3, j = 0 ; i < srclen && j < 4 ; i++, j++) { switch (j) { case 0: d->cols += (src[i] == IAC) ? src[i++] * 256 : src[i] * 256; break; case 1: d->cols += (src[i] == IAC) ? src[i++] : src[i]; break; case 2: d->rows += (src[i] == IAC) ? src[i++] * 256 : src[i] * 256; break; case 3: d->rows += (src[i] == IAC) ? src[i++] : src[i]; break; } } return skip_sb(d, src, srclen); } /* NEW ENVIRON, used here to discover Windows telnet. */ int process_will_new_environ( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ) { descriptor_printf(d, "%c%c%c%c%c%s%c%c", IAC, SB, TELOPT_NEW_ENVIRON, ENV_SEND, ENV_VAR, "SYSTEMTYPE", IAC, SE); return 3; } int process_sb_new_environ( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ) { char var[MAX_INPUT_LENGTH], val[MAX_INPUT_LENGTH]; char *pto = NULL; int i = 0; if (skip_sb(d, src, srclen) > srclen) { return srclen + 1; } var[0] = val[0] = 0; i = 4; while (i < srclen && src[i] != SE) { switch (src[i]) { case ENV_VAR: case ENV_USR: i++; pto = var; while (i < srclen && src[i] >= 32 && src[i] != IAC) { *pto++ = src[i++]; } *pto = 0; break; case ENV_VAL: i++; pto = val; while (i < srclen && src[i] >= 32 && src[i] != IAC) { *pto++ = src[i++]; } *pto = 0; if (!str_cmp(var, "SYSTEMTYPE") && !str_cmp(val, "WIN32")) { STRFREE( d->terminal_type ); d->terminal_type = STRALLOC( "WIN32" ); } break; default: i++; break; } } return i + 1; } /* * Data for MUD Server Status Protocol (MSSP). * * Please try following the established conventions as * outlined here: http://www.mudbytes.net/index.php?a=articles&s=MSSP_Fields */ static const char *MSSP_FIELD_MUD_NAME = "SWR2 Refactor Devel"; static const char *MSSP_FIELD_HOSTNAME = "swr2.totj.net"; static const char *MSSP_FIELD_IP = "97.107.139.29"; static const char *MSSP_FIELD_CODEBASE = "SWR2 Refactor"; static const char *MSSP_FIELD_CONTACT = "kai@swr2.totj.net"; static const char *MSSP_FIELD_CREATED = "2009"; static const char *MSSP_FIELD_ICON = ""; static const char *MSSP_FIELD_LANGUAGE = "English"; static const char *MSSP_FIELD_LOCATION = "United States"; static const char *MSSP_FIELD_MINIMUM_AGE = "13"; static const char *MSSP_FIELD_WEBSITE = "http://swr2.totj.net"; static const char *MSSP_FIELD_FAMILY = "DikuMUD"; static const char *MSSP_FIELD_GENRE = "Science Fiction"; static const char *MSSP_FIELD_SUBGENRE = "None"; static const char *MSSP_FIELD_GAMEPLAY = "Player versus Player"; static const char *MSSP_FIELD_GAMESYSTEM = "Custom"; static const char *MSSP_FIELD_INTERMUD = "IMC2"; static const char *MSSP_FIELD_STATUS = "Open Beta"; static const int MSSP_FIELD_LEVELS = 0; static const int MSSP_FIELD_RACES = 1; static const int MSSP_FIELD_ANSI = 1; static const int MSSP_FIELD_MCCP = 1; static const int MSSP_FIELD_MCP = 0; static const int MSSP_FIELD_MSP = 0; static const int MSSP_FIELD_MXP = 0; static const int MSSP_FIELD_PUEBLO = 0; static const int MSSP_FIELD_VT100 = 0; static const int MSSP_FIELD_XTERM256 = 0; static const int MSSP_FIELD_PAY_TO_PLAY = 0; static const int MSSP_FIELD_PAY_FOR_PERKS = 0; static const int MSSP_FIELD_HIRING_BUILDERS = 0; static const int MSSP_FIELD_HIRING_CODERS = 0; extern int port; /* MSSP: Mud Server Status Protocol */ int process_do_mssp( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ) { char buffer[MAX_STRING_LENGTH] = { 0 }; cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "NAME", MSSP_VAL, MSSP_FIELD_MUD_NAME); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "PLAYERS", MSSP_VAL, num_descriptors); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "UPTIME", MSSP_VAL, boot_time); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "HOSTNAME", MSSP_VAL, MSSP_FIELD_HOSTNAME); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "PORT", MSSP_VAL, port); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "CODEBASE", MSSP_VAL, MSSP_FIELD_CODEBASE); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "CONTACT", MSSP_VAL, MSSP_FIELD_CONTACT); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "CREATED", MSSP_VAL, MSSP_FIELD_CREATED); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "ICON", MSSP_VAL, MSSP_FIELD_ICON); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "IP", MSSP_VAL, MSSP_FIELD_IP); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "LANGUAGE", MSSP_VAL, MSSP_FIELD_LANGUAGE); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "LOCATION", MSSP_VAL, MSSP_FIELD_LOCATION); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "MINIMUM AGE", MSSP_VAL, MSSP_FIELD_MINIMUM_AGE); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "WEBSITE", MSSP_VAL, MSSP_FIELD_WEBSITE); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "FAMILY", MSSP_VAL, MSSP_FIELD_FAMILY); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "GENRE", MSSP_VAL, MSSP_FIELD_GENRE); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "GAMEPLAY", MSSP_VAL, MSSP_FIELD_GAMEPLAY); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "GAMESYSTEM", MSSP_VAL, MSSP_FIELD_GAMESYSTEM); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "INTERMUD", MSSP_VAL, MSSP_FIELD_INTERMUD); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "STATUS", MSSP_VAL, MSSP_FIELD_STATUS); cat_sprintf(buffer, "%c%s%c%s", MSSP_VAR, "SUBGENRE", MSSP_VAL, MSSP_FIELD_SUBGENRE); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "AREAS", MSSP_VAL, top_area); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "HELPFILES", MSSP_VAL, top_help); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "MOBILES", MSSP_VAL, top_mob_index); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "OBJECTS", MSSP_VAL, top_obj_index); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "ROOMS", MSSP_VAL, top_room); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "CLASSES", MSSP_VAL, skill_package_table_size() ); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "LEVELS", MSSP_VAL, MSSP_FIELD_LEVELS ); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "RACES", MSSP_VAL, MSSP_FIELD_RACES ); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "SKILLS", MSSP_VAL, top_sn); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "ANSI", MSSP_VAL, MSSP_FIELD_ANSI); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "MCCP", MSSP_VAL, MSSP_FIELD_MCCP); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "MCP", MSSP_VAL, MSSP_FIELD_MCP); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "MSP", MSSP_VAL, MSSP_FIELD_MSP); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "MXP", MSSP_VAL, MSSP_FIELD_MXP); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "PUEBLO", MSSP_VAL, MSSP_FIELD_PUEBLO); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "VT100", MSSP_VAL, MSSP_FIELD_VT100); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "XTERM 256 COLORS", MSSP_VAL, MSSP_FIELD_XTERM256); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "PAY TO PLAY", MSSP_VAL, MSSP_FIELD_PAY_TO_PLAY); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "PAY FOR PERKS", MSSP_VAL, MSSP_FIELD_PAY_FOR_PERKS); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "HIRING BUILDERS", MSSP_VAL, MSSP_FIELD_HIRING_BUILDERS); cat_sprintf(buffer, "%c%s%c%d", MSSP_VAR, "HIRING CODERS", MSSP_VAL, MSSP_FIELD_HIRING_CODERS); descriptor_printf(d, "%c%c%c%s%c%c", IAC, SB, TELOPT_MSSP, buffer, IAC, SE); return 3; } /* MCCP: Mud Client Compression Protocol */ void *zlib_alloc( void *opaque, unsigned int items, unsigned int size ) { return calloc(items, size); } void zlib_free( void *opaque, void *address ) { free(address); } int start_compress( DESCRIPTOR_DATA *d ) { const char start_mccp[] = { IAC, SB, TELOPT_MCCP, IAC, SE, 0 }; z_stream *stream = NULL; if (d->mccp) { return TRUE; } CREATE(stream, z_stream, 1); stream->next_in = NULL; stream->avail_in = 0; stream->next_out = sysdata.mccp_buf; stream->avail_out = COMPRESS_BUF_SIZE; stream->data_type = Z_ASCII; stream->zalloc = zlib_alloc; stream->zfree = zlib_free; stream->opaque = Z_NULL; /* 12, 5 = 32K of memory, more than enough */ if (deflateInit2(stream, Z_BEST_COMPRESSION, Z_DEFLATED, 12, 5, Z_DEFAULT_STRATEGY) != Z_OK) { log_printf("start_compress: failed deflateInit2 D%d@%s", d->descriptor, d->host); DISPOSE(stream); return FALSE; } write_to_descriptor(d, start_mccp, 0); /* The above call must send all pending output to the descriptor, since from now on we'll be compressing. */ d->mccp = stream; return TRUE; } void end_compress( DESCRIPTOR_DATA *d ) { if (d->mccp == NULL) { return; } d->mccp->next_in = NULL; d->mccp->avail_in = 0; d->mccp->next_out = sysdata.mccp_buf; d->mccp->avail_out = COMPRESS_BUF_SIZE; if (deflate(d->mccp, Z_FINISH) != Z_STREAM_END) { log_printf("end_compress: failed to deflate D%d@%s", d->descriptor, d->host); } process_compressed(d); if (deflateEnd(d->mccp) != Z_OK) { log_printf("end_compress: failed to deflateEnd D%d@%s", d->descriptor, d->host); } DISPOSE(d->mccp); d->mccp = NULL; } void write_compressed( DESCRIPTOR_DATA *d ) { d->mccp->next_in = (unsigned char*) d->outbuf; d->mccp->avail_in = d->outtop; d->mccp->next_out = sysdata.mccp_buf; d->mccp->avail_out = COMPRESS_BUF_SIZE; d->outtop = 0; if (deflate(d->mccp, Z_SYNC_FLUSH) != Z_OK) { return; } process_compressed(d); } void process_compressed( DESCRIPTOR_DATA *d ) { int length = COMPRESS_BUF_SIZE - d->mccp->avail_out; if (send(d->descriptor, sysdata.mccp_buf, length, 0) < 1) { log_printf("process_compressed D%d@%s", d->descriptor, d->host); return; } } int process_do_mccp( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ) { start_compress(d); return 3; } int process_dont_mccp( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ) { end_compress(d); return 3; } /* Returns the length of a telnet subnegotiation, return srclen + 1 for incomplete state. */ int skip_sb( DESCRIPTOR_DATA *d, unsigned char *src, int srclen ) { int i; for (i = 1 ; i < srclen ; i++) { if (src[i] == SE && src[i-1] == IAC) { return i + 1; } } return srclen + 1; } /* Utility function */ void descriptor_printf( DESCRIPTOR_DATA *d, const char *fmt, ... ) { char buf[MAX_STRING_LENGTH]; int size = 0; va_list args; va_start(args, fmt); size = vsprintf(buf, fmt, args); va_end(args); write_to_descriptor(d, buf, size); }