/* * $Id: prot-a-parse.c,v 0.32 1998/07/08 13:41:55 ceder Exp $ * Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996 Lysator Academic Computer Association. * * This file is part of the LysKOM server. * * LysKOM is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * LysKOM is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with LysKOM; see the file COPYING. If not, write to * Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN, * or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, * MA 02139, USA. * * Please mail bug reports to bug-lyskom@lysator.liu.se. */ /* * prot-a-parse.c - parse protocol-A messages. * * BUG: Not all functions are used, I think. /ceder */ static const char * rcsid = "$Id: prot-a-parse.c,v 0.32 1998/07/08 13:41:55 ceder Exp $"; #include "rcs.h" USE(rcsid); #include #include #include #include #include #include #ifdef HAVE_STDARG_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #include "debug.h" #include "s-string.h" #include "kom-types.h" #include "server/smalloc.h" #include "com.h" #include "async.h" #include "connections.h" #include "prot-a-parse.h" #include "isc-parse.h" #include "config.h" #include "isc-interface.h" #include "mux.h" #include "log.h" #include "minmax.h" #include "param.h" BUGDECL; /* * Return next token from the input stream. Note that the String returned * by this call points into data that might be freed by the next call to * get_token or any function which reads from the stream. */ static String prot_a_get_token(Connection *client) { String result; String_size old_first; old_first = client->first_to_parse; result = s_strtok(client->unparsed, &client->first_to_parse, s_fcrea_str(WHITESPACE)); /* Check that there was at least one trailing blank. */ if ( client->first_to_parse >= s_strlen(client->unparsed) ) { if (client->first_to_parse - old_first > 1000) { mux_printf(client, "%%%%Insane token length.\n"); mux_flush(client); longjmp(parse_env, ISC_LOGOUT); } client->first_to_parse = old_first; longjmp(parse_env, ISC_MSG_INCOMPLETE); } return result; } long prot_a_parse_long(Connection *client) { String token; String_size end; long res; token = prot_a_get_token(client); res = s_strtol(token, &end, PROTOCOL_NUMBER_BASE); if (end != s_strlen(token)) longjmp(parse_env, ISC_PROTOCOL_ERR); return res; } void prot_a_parse_num_list(Connection *client, Number_list *res, int maxlen) { static unsigned long err_cnt = 0; switch (client->array_parse_pos) { case 0: /* The array size. */ /* This could just as well have been an assertion. */ if ((res->length != 0 || res->data != NULL) && err_cnt++ < 20) { log("WNG: prot_a_parse_num_list(): len = %lu data = %lu\n", (unsigned long)res->length, (unsigned long)res->data); res->length = 0; res->data = NULL; if (err_cnt == 20) log("The above warning is now turned off."); } res->length = prot_a_parse_long(client); if (res->length < 0) { mux_printf(client, "%%%%Insane array size.\n"); mux_flush(client); longjmp(parse_env, ISC_LOGOUT); } client->array_parse_pos = 1; /* Fall through */ case 1: /* The opening curly brace. */ if ( parse_nonwhite_char(client) != '{' ) longjmp(parse_env, ISC_PROTOCOL_ERR); if (res->length <= maxlen) res->data = smalloc(sizeof(*res->data) * res->length); else { assert(res->data == NULL); } client->array_parse_index = 0; client->array_parse_pos = 2; /* Fall through */ case 2: /* The elements in the array. */ while (client->array_parse_index < res->length) { long tmp = prot_a_parse_long(client); if (res->data != NULL) res->data[client->array_parse_index] = tmp; ++client->array_parse_index; } client->array_parse_pos = 3; /* Fall through */ case 3: /* Closing brace. */ if ( parse_nonwhite_char(client) != '}' ) longjmp(parse_env, ISC_PROTOCOL_ERR); default: client->array_parse_pos = 0; } } void prot_a_parse_priv_bits(Connection *client, Priv_bits *res) { String token; token = prot_a_get_token(client); if ( s_strlen(token) != 16 ) longjmp(parse_env, ISC_PROTOCOL_ERR); res->wheel = token.string[ 0 ] != '0'; res->admin = token.string[ 1 ] != '0'; res->statistic = token.string[ 2 ] != '0'; res->create_pers = token.string[ 3 ] != '0'; res->create_conf = token.string[ 4 ] != '0'; res->change_name = token.string[ 5 ] != '0'; res->extern_gw = token.string[ 6 ] != '0'; res->flg8 = token.string[ 7 ] != '0'; res->flg9 = token.string[ 8 ] != '0'; res->flg10 = token.string[ 9 ] != '0'; res->flg11 = token.string[ 10 ] != '0'; res->flg12 = token.string[ 11 ] != '0'; res->flg13 = token.string[ 12 ] != '0'; res->flg14 = token.string[ 13 ] != '0'; res->flg15 = token.string[ 14 ] != '0'; res->flg16 = token.string[ 15 ] != '0'; } void prot_a_parse_membership_type(Connection *client, Membership_type *res) { String token; token = prot_a_get_token(client); if (s_strlen(token) != 8) longjmp(parse_env, ISC_PROTOCOL_ERR); res->invitation = token.string[ 0 ] != '0'; res->passive = token.string[ 1 ] != '0'; res->secret = token.string[ 2 ] != '0'; res->reserved1 = token.string[ 3 ] != '0'; res->reserved2 = token.string[ 4 ] != '0'; res->reserved3 = token.string[ 5 ] != '0'; res->reserved4 = token.string[ 6 ] != '0'; res->reserved5 = token.string[ 7 ] != '0'; } void prot_a_parse_conf_type(Connection *client, Conf_type *res) { String token; token = prot_a_get_token(client); if ( s_strlen(token) != 4 && s_strlen(token) != 8) longjmp(parse_env, ISC_PROTOCOL_ERR); res->rd_prot = token.string[ 0 ] != '0'; res->original = token.string[ 1 ] != '0'; res->secret = token.string[ 2 ] != '0'; res->letter_box = token.string[ 3 ] != '0'; if (s_strlen(token) != 8) { res->allow_anon = 0; res->reserved1 = 0; res->reserved2 = 0; res->reserved3 = 0; } else { res->allow_anon = token.string[ 4 ] != '0'; res->reserved1 = token.string[ 5 ] != '0'; res->reserved2 = token.string[ 6 ] != '0'; res->reserved3 = token.string[ 7 ] != '0'; } } /* * Parse a string. At most 'maxlen' characters are allowed. If the * client sends a longer string only the first 'maxlen+1' characters * are read. Any remaining characters are discarded. */ /* * +++ This needs cleaning up. See comments in and above mux_parse_string. */ void prot_a_parse_string(Connection *client, String *result, int maxlen) { String_size hptr; /* Pointer to 'H' */ String_size client_len; /* The len the client is sending. */ String_size truncated_len; /* How much the server will receive. */ String_size to_skip; static unsigned long err_cnt = 0; switch ( client->string_parse_pos ) { case 0: if ( (result->len != 0 || result->string != NULL) && err_cnt++ < 20 ) { log ("%s == %lu, result->string == %lu. %s\n", "prot_a_parse_string(): result->len", (unsigned long)result->len, (unsigned long)result->string, "This memory will not be free()'d."); *result = EMPTY_STRING; if ( err_cnt == 20 ) log("Won't log the above warning no more."); } /* Get number and discard trailing 'H' */ client_len = s_strtol(s_fsubstr(client->unparsed, client->first_to_parse, END_OF_STRING), &hptr, PROTOCOL_NUMBER_BASE); if ( hptr == -1 || client->first_to_parse + hptr >= s_strlen(client->unparsed) ) { longjmp(parse_env, ISC_MSG_INCOMPLETE); } if (client_len < 0) { mux_printf(client, "%%%%Insane string length.\n"); mux_flush(client); BUG(("%%%%Insane string length.\n")); longjmp(parse_env, ISC_LOGOUT); } /* Check that a) there is a trailing H b) there was at least one digit before the H */ if ( client->unparsed.string[ client->first_to_parse + hptr ] != 'H' || hptr <= 0 ) { longjmp(parse_env, ISC_PROTOCOL_ERR); } client->first_to_parse += 1 + hptr; client->string_parse_pos = 1; result->len = client_len; /* +++ Transfer */ /* Fall through */ case 1: client_len = result->len; /* Check that the entire string is transmitted. */ /* (Don't care about the trailing part that will be skipped if the * string is longer than maxlen) */ truncated_len = min(maxlen + 1, client_len); if ( client->first_to_parse + truncated_len > s_strlen(client->unparsed) ) { longjmp(parse_env, ISC_MSG_INCOMPLETE); } *result = EMPTY_STRING; s_mem_crea_str(result, client->unparsed.string + client->first_to_parse, truncated_len); result->len = client_len; /* Ugly! +++ */ client->first_to_parse += truncated_len; client->string_parse_pos = 2; /* Fall through */ case 2: /* Was the string too long? If so, skip the truncated data. */ client_len = result->len; truncated_len = min(maxlen+1, client_len); if ( client_len > truncated_len ) { to_skip = min(client_len - truncated_len, client->unparsed.len - client->first_to_parse); client_len -= to_skip; client->first_to_parse += to_skip; } result->len = client_len; if ( client_len > truncated_len ) longjmp(parse_env, ISC_MSG_INCOMPLETE); /* Fall through */ default: client->string_parse_pos = 0; } } extern void prot_a_parse_aux_item_flags(Connection *client, Aux_item_flags *res) { String token; token = prot_a_get_token(client); if ( s_strlen(token) != 8 ) longjmp(parse_env, ISC_PROTOCOL_ERR); res->deleted = token.string[ 0 ] != '0'; res->inherit = token.string[ 1 ] != '0'; res->secret = token.string[ 2 ] != '0'; res->hide_creator = token.string[ 3 ] != '0'; res->dont_garb = token.string[ 4 ] != '0'; res->reserved3 = token.string[ 5 ] != '0'; res->reserved4 = token.string[ 6 ] != '0'; res->reserved5 = token.string[ 7 ] != '0'; } extern void prot_a_parse_aux_item(Connection *client, Aux_item *result) { switch ( client->struct_parse_pos ) { case 0: result->aux_no = prot_a_parse_long(client); client->struct_parse_pos = 1; case 1: result->tag = prot_a_parse_long(client); client->struct_parse_pos = 2; case 2: prot_a_parse_aux_item_flags(client, &result->flags); client->struct_parse_pos = 3; case 3: result->inherit_limit = prot_a_parse_long(client); client->struct_parse_pos = 4; case 4: prot_a_parse_string(client, &(result->data), param.text_len); default: client->struct_parse_pos = 0; } } extern void prot_a_parse_aux_item_list(Connection *client, Aux_item_list *result, unsigned long maxlen) { int i; switch (client->array_parse_pos) { case 0: result->length = prot_a_parse_long(client); client->array_parse_pos = 1; case 1: if ( parse_nonwhite_char(client) != '{' ) longjmp(parse_env, ISC_PROTOCOL_ERR); if ( result->length > maxlen ) longjmp(parse_env, ISC_PROTOCOL_ERR); result->items = smalloc(result->length * sizeof(Aux_item)); for (i = 0; i < result->length; i++) { result->items[i].data = EMPTY_STRING; } client->array_parse_index = 0; client->array_parse_pos = 2; case 2: while( client->array_parse_index < result->length ) { prot_a_parse_aux_item(client, &result->items[client->array_parse_index]); client->array_parse_index += 1; } client->array_parse_pos = 3; case 3: if ( parse_nonwhite_char(client) != '}' ) longjmp(parse_env, ISC_PROTOCOL_ERR); default: client->array_parse_pos = 0; } } extern void prot_a_parse_misc_info(Connection *client, Misc_info *result) { switch ( client->struct_parse_pos ) { case 0: result->type = (Info_type)prot_a_parse_long(client); client->struct_parse_pos = 1; /* Fall through */ case 1: switch( result->type ) { case recpt: result->datum.recipient = prot_a_parse_long(client); break; case cc_recpt: result->datum.cc_recipient = prot_a_parse_long(client); break; case bcc_recpt: result->datum.bcc_recipient = prot_a_parse_long(client); break; case loc_no: result->datum.local_no = prot_a_parse_long(client); break; case comm_to: result->datum.comment_to = prot_a_parse_long(client); break; case footn_to: result->datum.footnote_to = prot_a_parse_long(client); break; case comm_in: case footn_in: case rec_time: case sent_by: case sent_at: /* Fall through */ #ifndef COMPILE_CHECKS default: #endif longjmp(parse_env, ISC_PROTOCOL_ERR); } default: client->struct_parse_pos = 0; } } void prot_a_parse_time_date(Connection *client, struct tm *result) { /* A time never occur inside a string, and a string never occur inside a time. Thus I use string_parse_pos here instead of inventing a new variable. Lacgen will allocate variables as needed... */ switch( client->string_parse_pos ) { case 0: result->tm_sec = prot_a_parse_long(client); client->string_parse_pos = 1; /* Fall through */ case 1: result->tm_min = prot_a_parse_long(client); client->string_parse_pos = 2; /* Fall through */ case 2: result->tm_hour = prot_a_parse_long(client); client->string_parse_pos = 3; /* Fall through */ case 3: result->tm_mday = prot_a_parse_long(client); client->string_parse_pos = 4; /* Fall through */ case 4: result->tm_mon = prot_a_parse_long(client); client->string_parse_pos = 5; /* Fall through */ case 5: result->tm_year = prot_a_parse_long(client); client->string_parse_pos = 6; /* Fall through */ case 6: result->tm_wday = prot_a_parse_long(client); client->string_parse_pos = 7; /* Fall through */ case 7: result->tm_yday = prot_a_parse_long(client); client->string_parse_pos = 8; /* Fall through */ case 8: result->tm_isdst = prot_a_parse_long(client); /* Fall through */ default: client->string_parse_pos = 0; } } void prot_a_parse_info(Connection *client, Info *result) { switch( client->struct_parse_pos ) { case 0: result->version = prot_a_parse_long(client); client->struct_parse_pos = 1; /* Fall through */ case 1: result->conf_pres_conf = prot_a_parse_long(client); client->struct_parse_pos = 2; /* Fall through */ case 2: result->pers_pres_conf = prot_a_parse_long(client); client->struct_parse_pos = 3; /* Fall through */ case 3: result->motd_conf = prot_a_parse_long(client); client->struct_parse_pos = 4; /* Fall through */ case 4: result->kom_news_conf = prot_a_parse_long(client); client->struct_parse_pos = 5; /* Fall through */ case 5: result->motd_of_lyskom = prot_a_parse_long(client); /* Fall through */ default: client->struct_parse_pos = 0; } } void prot_a_hunt_nl(Connection *client) { String_size number_end; String_size len; while (1) { switch (client->fnc_parse_pos) { case 0: /* whitspace/simple tokens */ if ( client->first_to_parse >= s_strlen(client->unparsed) ) longjmp(parse_env, ISC_MSG_INCOMPLETE); switch(client->unparsed.string[client->first_to_parse]) { case ' ': case '\r': case '\t': case '{': case '}': case '*': client->first_to_parse++; break; case '\n': /* We found a newline -- end of message. Return now. */ client->first_to_parse++; return; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* Entering a number -- possibly a string. */ /* Don't increase first_to_parse. */ client->fnc_parse_pos = 1; break; default: longjmp(parse_env, ISC_PROTOCOL_ERR); } break; case 1: /* number/string */ /* Entering a number. The first char is known to be a digit. Parse the entire number at once. */ len = client->unparsed.string[client->first_to_parse] - '0'; for (number_end = client->first_to_parse + 1; number_end < s_strlen(client->unparsed); number_end++) { if (client->unparsed.string[number_end] >= '0' && client->unparsed.string[number_end] <= '9') { len = 10 * len + client->unparsed.string[number_end] - '0'; } else { /* The end of the number was reached. */ break; } } if (number_end == s_strlen(client->unparsed)) longjmp(parse_env, ISC_MSG_INCOMPLETE); if (client->unparsed.string[number_end] == 'H') { /* We are entering a string. Use num0 to store the lenght of the string to skip. */ client->num0 = len; client->first_to_parse = number_end + 1; client->fnc_parse_pos = 2; } else { /* We have just skipped past a number that was not the start of a string. */ client->first_to_parse = number_end; client->fnc_parse_pos = 0; } break; case 2: /* Skipping a string. */ if (client->num0 == 0) { /* The entire string has been skipped. */ client->fnc_parse_pos = 0; break; } len = s_strlen(client->unparsed) - client->first_to_parse; if (client->num0 <= len) { /* The entire string is present. Skip it. */ client->first_to_parse += client->num0; client->fnc_parse_pos = 0; break; } else { /* Skip as much of the string as possible. */ client->num0 -= len; client->first_to_parse = s_strlen(client->unparsed); longjmp(parse_env, ISC_MSG_INCOMPLETE); } abort(); default: abort(); } } }