/* $Id: tdu_log.c,v 1.2 2001/03/01 14:59:12 paul Exp $ */ /* Copyright 1997 by Henner Eisen This code is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This code 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include "tdu.h" #include "fileheader.h" char * (*tdu_extended_reason)(int) = NULL; /* Table with various information on tdu protocol data units */ static struct tdu_descr cmd_descr[] = { {"T_RESPONSE_POSITIVE", TDU_CI_T_RESPONSE_POSITIVE,0}, {"T_RESPONSE_NEGATIVE", TDU_CI_T_RESPONSE_NEGATIVE,0}, {"T_ASSOCIATE", TDU_CI_T_ASSOCIATE,0}, {"T_RELEASE", TDU_CI_T_RELEASE,0}, {"T_ABORT", TDU_CI_T_ABORT,0}, {"T_ACCESS", TDU_CI_T_ACCESS,1}, {"T_END_ACCESS", TDU_CI_T_END_ACCESS,1}, {"T_DIRECTORY", TDU_CI_T_DIRECTORY,1}, {"T_LOAD", TDU_CI_T_LOAD,1}, {"T_SAVE", TDU_CI_T_SAVE,1}, {"T_RENAME", TDU_CI_T_RENAME,1}, {"T_DELETE", TDU_CI_T_DELETE,1}, {"T_TYPED_DATA", TDU_CI_T_TYPED_DATA,1}, {"T_WRITE", TDU_CI_T_WRITE,0}, {"T_TRANSFER_REJECT", TDU_CI_T_TRANSFER_REJECT,0}, {"T_READ_RESTART", TDU_CI_T_READ_RESTART,0}, {"T_P_EXCEPTION", TDU_CI_T_P_EXCEPTION,1}, {"RESPONSE_TIMEOUT", TDU_CI_RESPONSE_TIMEOUT,2}, {"SOCKET_READ_ERROR", TDU_CI_SOCK_READ_ERROR,2}, {"TC disconnected", TDU_CI_TC_DISCONNECT,2}, {"Empty TDU", TDU_CI_EMPTY,2}, {NULL, 0,0} }; /* Table with various information on result/reason codes. According to ETS 300 075, 7.2.1 and 7.2.2*/ static struct tdu_descr tdu_res_descr[] = { {"provider: called address incorrect", TDU_RE_CALLED_ADDR_INCOR_P,0}, {"user: called address incorrect", TDU_RE_CALLED_ADDR_INCOR_U,0}, {"provider: calling address incorrect", TDU_RE_CALLING_ADDR_INCOR_P,0}, {"user: calling address incorrect", TDU_RE_CALLING_ADDR_INCOR_U,0}, {"provider: role refused", TDU_RE_ROLE_REFUSED_P,0}, {"user: role refused", TDU_RE_ROLE_REFUSED_U,0}, {"insufficient primitives handled", TDU_RE_INSUFF_PRIMITIVES,0}, {"application name unknown", TDU_RE_APPL_NAME_UNKNOWN,0}, {"provider: service class refused", TDU_RE_SVC_CLASS_REFUSED_P,0}, {"user: service class refused", TDU_RE_SVC_CLASS_REFUSED_U,0}, {"erroneous revovery point", TDU_RE_ERRON_RECOV_POINT,0}, {"erroneous designation", TDU_RE_ERRON_DESIGN,0}, {"no answer to the request", TDU_RE_NO_ANSWER,0}, {"unknown file", TDU_RE_UNKNOWN_FILE,0}, {"already existing file", TDU_RE_FILE_EXISTS,0}, {"erroneous file", TDU_RE_ERRON_FILE,0}, {"erroneous new name", TDU_RE_ERRON_NEW_NAME,0}, {"new name already in use", TDU_RE_NEW_NAME_IN_USE,0}, {"wrong identity/identification", TDU_RE_WRONG_ID,0}, {"erroneous user data", TDU_RE_ERRON_UDATA,0}, {"service unknown", TDU_RE_SVC_UNKNOWN,0}, {"group forbidden", TDU_RE_GROUP_FORBIDDEN,0}, {"other reason", TDU_RE_OTHER_REASON,0}, {"repeated errors/negative acknowledges",TDU_RE_REPEATED_ERROR,0}, {"delay expired", TDU_RE_DELAY_EXPIRED,0}, {"unknown message", TDU_RE_UNKNOWN_MESSAGE,0}, {"syntax error/missing parameter", TDU_RE_SYNTAX_ERROR,0}, {"unrecoverable lower layer error", TDU_RE_LOWER_LAYER_ERROR,0}, {"protocol conflict", TDU_RE_PROTOCOL_CONFLICT,0}, {"primitive not handled", TDU_RE_PRIMITIVE_NOT_HANDLED,0}, {NULL, 0,0} }; /* Table with various information on tdu parameters */ static struct tdu_descr tdu_par_descr[] = { {"USER_DATA", TDU_PI_USER_DATA,0}, {"CALLED_ADDRESS", TDU_PI_CALLED_ADDRESS,1}, {"CALLING_ADDRESS", TDU_PI_CALLING_ADDRESS,1}, {"Result/Reason", TDU_PI_RESULT_REASON,1}, {"Role/Function", TDU_PI_ROLE_FUNCTION,0}, {"APPLICATION_NAME", TDU_PI_APPLICATION_NAME,0}, {"APPL_RESPONSE_TIMOUT", TDU_PI_APPL_RESPONSE_TIMEOUT,0}, {"Size/Recovery/Window", TDU_PI_SIZE_RECOVERY_WINDOW,1}, {"DESIGNATION", TDU_PI_DESIGNATION,1}, {"NEW_NAME", TDU_PI_NEW_NAME,1}, {"REQUEST_IDENT", TDU_PI_REQUEST_IDENT,1}, {"Identification", TDU_PI_IDENTIFICATION,1}, {"ExplCnf/1st/last/bl#", TDU_PI_EX_CONF_1ST_LAST_BLNO,0}, {"TRANSFER_MODE", TDU_PI_TRANSFER_MODE,1}, {"RECOVERY_POINT", TDU_PI_RECOVERY_POINT,1}, {"SERVICE_CLASS", TDU_PI_SERVICE_CLASS,0}, /* not a parameter but compatible */ {"File_Header", TDU_PI_FILE_HEADER,0}, {NULL, 0,0} }; const char * tdu_cmd_descr(int ci) { static const char unknown[] = "Unknown_TDU_Command"; char * des = tdu_des(cmd_descr,ci); return des ? des : unknown; } const char * tdu_param_descr(int pi) { static const char unknown[] = "Unknown_TDU_Parameter"; char* des = tdu_des(tdu_par_descr,pi); return des ? des : unknown; } FILE * tdu_logfile; unsigned int tdu_stderr_mask = 0, tdu_logfile_mask = 0, tdu_stderr_dirty = 0, nl_err=1, nl_log=1; pid_t log_pid=0; char log_prefix[20]="e4l[%d]:%s: ", time_format[20]="%Y-%m-%d %H:%M:%S"; int tdu_open_log(const char * fname) { tdu_logfile = fopen(fname,"a"); if( ! tdu_logfile ) { perror("fopen log file"); tdu_logfile_mask = 0; } else { setvbuf(tdu_logfile,NULL,_IOLBF,4098); } return tdu_logfile != NULL; } /* * set/change log prefix and date format */ int tdu_log_prefix(const char * id_fmt, const char *tm_fmt) { if( id_fmt) strncpy(log_prefix, id_fmt, sizeof(log_prefix)); if( tm_fmt) strncpy(time_format, tm_fmt, sizeof(time_format)); log_pid = getpid(); return 0; } int tdu_printf( int context, char * fmt, ... ) { int ret, nl, i; va_list ap; char tst[20]; /* * Check for trailing newline might fail if newline is part of last * argument. It is also not bullet proof if several processes * are writing to the same output channel in parallel. Setting * line buffered io mode should help for most scenarios. */ nl = ( fmt[strlen(fmt)-1] == '\n' ); if( nl_err || nl_log ){ struct tm * tm_time; time_t t=0; time(&t); tm_time = localtime(&t); i = strftime(tst, sizeof(tst), time_format, tm_time); } va_start(ap, fmt); if( context & tdu_stderr_mask ){ if( nl_err ) fprintf(stderr, log_prefix, log_pid, tst); ret = vfprintf(stderr, fmt, ap); tdu_stderr_dirty = 1; nl_err = nl; } if( ret < 0 ) return ret; va_start(ap, fmt); if( context & tdu_logfile_mask ){ if( nl_log ) fprintf(tdu_logfile, log_prefix, log_pid, tst); ret = vfprintf(tdu_logfile, fmt, ap); nl_log = nl; } va_end(ap); return ret; } void tdu_prompt(char * prompt) { fprintf(stderr,prompt); tdu_stderr_dirty = 0; } unsigned char * tdu_print_cmd( int ct, unsigned char *pkt, unsigned char *end, int level ) { unsigned char ci, ci2; int len=0, garbage=0; if( pkt >= end ) goto packet_too_small; if( level < 1 ) { tdu_printf(ct,"\n"); return pkt; } ci = *(pkt++); tdu_printf(ct, " %s(", tdu_cmd_descr(ci)); if( pkt < end ) len = *(pkt++); if( len == 0xff ) { if( pkt+2 >= end ) goto packet_too_small; len = 0x100 * *(pkt) + *(pkt+1); pkt += 2; } if( pkt+len < end ){ garbage = end - pkt - len; /* remove trailing garbage */ end = pkt + len; } if( ci == TDU_CI_T_RESPONSE_POSITIVE || ci == TDU_CI_T_RESPONSE_NEGATIVE ){ if( pkt+2 > end ) goto packet_too_small; ci2 = *(pkt+2); tdu_printf(ct, "%s,", tdu_cmd_descr(ci2)); } tdu_printf(ct, "len=%d", len); if ( garbage ) tdu_printf(ct,"[+%d]", garbage); tdu_printf(ct, ")" ); if( level < 2 ) { tdu_printf(ct,"\n"); return pkt; } tdu_printf(ct, ": " ); /* Never log t-write contents */ if( ci == TDU_CI_T_WRITE ) { pkt = tdu_print_par( ct, pkt, end, ci, level ); tdu_printf(ct,"\n"); return end; } while( pkt < end ) pkt = tdu_print_par( ct, pkt, end, ci, level ); tdu_printf(ct,"\n"); return pkt; packet_too_small : tdu_printf(ct, "CMD packet to small!\n" ); return end; } void tdu_print_hex( int ct, unsigned char * pkt, unsigned char * end) { tdu_printf(ct,"0x"); while ( pkt < end ) tdu_printf(ct, "%02x ", (int) *(pkt++) ); } void tdu_print_txt( int ct, unsigned char * pkt, unsigned char * end) { unsigned char tmp; while ( pkt < end ){ tmp = *pkt & 0x7f; if( tmp < 32 ){ tdu_printf(ct, "^%c", (int) ( *pkt + 64 ) ); } else if ( *pkt == 0x7f ) { tdu_printf(ct, "^?" ); } else { tdu_printf(ct, "%c", (int) *pkt ); } pkt++; }; } void tdu_print_li( int ct, unsigned char * pkt, unsigned char * end) { int i = 0; while ( pkt < end ) { i = i * 256; i += *(pkt++); } tdu_printf(ct, "%d", i ); } static void tdu_print_srw( int ct, unsigned char * pkt, unsigned char * end) { int log2, size=512; if ( pkt >= end ) return; /* packet size */ log2 = (0x0e & *pkt) >> 1; while( log2-- ) size <<= 1; tdu_printf(ct,"%d/", size); /* recovery */ tdu_printf(ct,"%sAccepted/", (*pkt & 0x01) ? "" : "Not"); /* window size */ tdu_printf(ct,"%d", ((*pkt & 0xe0) >> 5) + 1 ); } static void tdu_print_mode( int ct, unsigned char * pkt, unsigned char * end) { if ( pkt >= end ) return; tdu_printf(ct,"%sBasicMode", (*pkt & 0x01) ? "" : "No"); } static void tdu_print_role( int ct, unsigned char * pkt, unsigned char * end) { if ( pkt >= end ) return; if( *pkt & 0x01 ) { tdu_printf(ct, "Master(" ); if (*pkt & 0x02) tdu_printf(ct," ReadRestart"); if (*pkt & 0x04) tdu_printf(ct," TypedData"); } else { tdu_printf(ct, "Slave(" ); if (*pkt & 0x02) tdu_printf(ct," ReadRestart"); if (*pkt & 0x04) tdu_printf(ct," TypedData"); if (*pkt & 0x08) tdu_printf(ct," Directory"); if (*pkt & 0x10) tdu_printf(ct," Delete"); if (*pkt & 0x20) tdu_printf(ct," Rename"); if (*pkt & 0x40) tdu_printf(ct," Save"); if (*pkt & 0x80) tdu_printf(ct," Load"); } tdu_printf(ct," )"); } /* FIXME: should not go into this source file */ #include "eft.h" static void tdu_print_reason( int ct, unsigned char * pkt, unsigned char * end, int ci) { int rci; if( pkt < end && ( ci == TDU_CI_T_RESPONSE_NEGATIVE || ci == TDU_CI_T_RESPONSE_POSITIVE ) ) { rci = *(pkt++); tdu_printf(ct, "%s ", tdu_cmd_descr( rci ) ); if( rci == TDU_CI_T_WRITE ){ int count = 0; if( pkt < end ) count += *(pkt++); if( pkt < end ) { count *= 256; count += *(pkt++);} tdu_printf(ct, "packet No %d",count); } } while ( pkt < end ){ tdu_printf(ct, "%s ", tdu_str_reason(*pkt) ); if( *pkt == TDU_RE_ERRON_UDATA && (pkt+1 < end)){ tdu_print_reason( ct, pkt+1, end, ci); return; } if( *pkt == TDU_RE_OTHER_REASON ){ char * ext_re = NULL; pkt++; /* FIXME: should not belong here */ if( (pkt+1) == end && tdu_extended_reason) ext_re = tdu_extended_reason(*pkt); while ( pkt < end ) tdu_printf(ct, "%c", (int) *(pkt++) ); tdu_printf(ct," "); if( ext_re ) tdu_printf(ct, "[%s] ",ext_re); }; pkt++; }; } unsigned char * tdu_print_par( int ct, unsigned char *pkt, unsigned char *end, int ci, int level ) { unsigned char pi, *ret; int len=0; if( pkt >= end ) goto packet_too_small; pi = *(pkt++); tdu_printf(ct, " %s(", tdu_param_descr(pi)); if( pkt >= end ) goto packet_too_small; if( (len = *(pkt++)) == 0xff ) { if( pkt+2 > end ) goto packet_too_small; len = 0x100 * *(pkt++) + *(pkt++); } tdu_printf(ct,"len=%d)", len); ret = pkt + len; if( level < 3 ) { return ret; } tdu_printf(ct,"="); switch ( pi ) { case TDU_PI_RESULT_REASON: tdu_print_reason(ct,pkt,ret,ci); break; case TDU_PI_SIZE_RECOVERY_WINDOW: tdu_print_srw(ct,pkt,ret); break; case TDU_PI_ROLE_FUNCTION: tdu_print_role(ct,pkt,ret); break; case TDU_PI_TRANSFER_MODE: tdu_print_mode(ct,pkt,ret); break; case TDU_PI_APPLICATION_NAME: case TDU_PI_CALLED_ADDRESS: case TDU_PI_CALLING_ADDRESS: case TDU_PI_DESIGNATION: case TDU_PI_NEW_NAME: case TDU_PI_IDENTIFICATION: tdu_print_txt(ct,pkt,ret); break; default: tdu_print_hex(ct,pkt,ret ); }; return ret; packet_too_small : tdu_printf(ct, "PAR packet to small!\n" ); return end; } const char * tdu_str_reason(int reason) { static const char unknown[] = "Unknown_TDU_ReasonCode"; char * des = tdu_des(tdu_res_descr,reason); return des ? des : unknown; } /* * Replace unprintable characters from string type tdu parameters. * Thus should be applied before printing those as strings in order * to prevent them from screwing up your terminal state. * * The data array must be at least dimensioned max(len+1,1), which is essential * to be verified before calling in order to prevent buffer overflow attacks. * The last data[len] (or data[0] if len<0) will be terminated by zero on exit. */ int tdu_mk_printable(unsigned char * pr_data, const unsigned char * in_data, int len){ int i; if( len < 0 ) { pr_data[0]=0; return 0; } pr_data[len]=0; memcpy(pr_data, in_data, len); for(i=0; i < len; i++){ if( ((0x7f & pr_data[i]) < 32) || (pr_data[i] == 0x7f) ) pr_data[i]='?'; } return 0; }