logging.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <ctype.h>
  4. #include <time.h>
  5. #include <assert.h>
  6. #include "putty.h"
  7. /* log session to file stuff ... */
  8. struct LogContext {
  9. FILE *lgfp;
  10. Filename currlogfilename;
  11. void *frontend;
  12. Config cfg;
  13. };
  14. static void xlatlognam(Filename *d, Filename s, char *hostname, struct tm *tm);
  15. /*
  16. * Log session traffic.
  17. */
  18. void logtraffic(void *handle, unsigned char c, int logmode)
  19. {
  20. struct LogContext *ctx = (struct LogContext *)handle;
  21. if (ctx->cfg.logtype > 0) {
  22. if (ctx->cfg.logtype == logmode) {
  23. /* deferred open file from pgm start? */
  24. if (!ctx->lgfp)
  25. logfopen(ctx);
  26. if (ctx->lgfp)
  27. fputc(c, ctx->lgfp);
  28. }
  29. }
  30. }
  31. /*
  32. * Flush any open log file.
  33. */
  34. void logflush(void *handle) {
  35. struct LogContext *ctx = (struct LogContext *)handle;
  36. if (ctx->cfg.logtype > 0)
  37. if (ctx->lgfp)
  38. fflush(ctx->lgfp);
  39. }
  40. /*
  41. * Log an Event Log entry. Used in SSH packet logging mode; this is
  42. * also as convenient a place as any to put the output of Event Log
  43. * entries to stderr when a command-line tool is in verbose mode.
  44. * (In particular, this is a better place to put it than in the
  45. * front ends, because it only has to be done once for all
  46. * platforms. Platforms which don't have a meaningful stderr can
  47. * just avoid defining FLAG_STDERR.
  48. */
  49. void log_eventlog(void *handle, const char *event)
  50. {
  51. struct LogContext *ctx = (struct LogContext *)handle;
  52. if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) {
  53. fprintf(stderr, "%s\n", event);
  54. fflush(stderr);
  55. }
  56. if (ctx->cfg.logtype != LGTYP_PACKETS)
  57. return;
  58. if (!ctx->lgfp)
  59. logfopen(ctx);
  60. if (ctx->lgfp)
  61. fprintf(ctx->lgfp, "Event Log: %s\r\n", event);
  62. }
  63. /*
  64. * Log an SSH packet.
  65. * If n_blanks != 0, blank or omit some parts.
  66. * Set of blanking areas must be in increasing order.
  67. */
  68. void log_packet(void *handle, int direction, int type,
  69. char *texttype, void *data, int len,
  70. int n_blanks, const struct logblank_t *blanks)
  71. {
  72. struct LogContext *ctx = (struct LogContext *)handle;
  73. char dumpdata[80], smalldata[5];
  74. if (ctx->cfg.logtype != LGTYP_PACKETS)
  75. return;
  76. if (!ctx->lgfp)
  77. logfopen(ctx);
  78. if (ctx->lgfp) {
  79. int p = 0, b = 0, omitted = 0;
  80. int output_pos = 0; /* NZ if pending output in dumpdata */
  81. /* Packet header. */
  82. fprintf(ctx->lgfp, "%s packet type %d / 0x%02x (%s)\r\n",
  83. direction == PKT_INCOMING ? "Incoming" : "Outgoing",
  84. type, type, texttype);
  85. /*
  86. * Output a hex/ASCII dump of the packet body, blanking/omitting
  87. * parts as specified.
  88. */
  89. while (p < len) {
  90. int blktype;
  91. /* Move to a current entry in the blanking array. */
  92. while ((b < n_blanks) &&
  93. (p >= blanks[b].offset + blanks[b].len))
  94. b++;
  95. /* Work out what type of blanking to apply to
  96. * this byte. */
  97. blktype = PKTLOG_EMIT; /* default */
  98. if ((b < n_blanks) &&
  99. (p >= blanks[b].offset) &&
  100. (p < blanks[b].offset + blanks[b].len))
  101. blktype = blanks[b].type;
  102. /* If we're about to stop omitting, it's time to say how
  103. * much we omitted. */
  104. if ((blktype != PKTLOG_OMIT) && omitted) {
  105. fprintf(ctx->lgfp, " (%d byte%s omitted)\r\n",
  106. omitted, (omitted==1?"":"s"));
  107. omitted = 0;
  108. }
  109. /* (Re-)initialise dumpdata as necessary
  110. * (start of row, or if we've just stopped omitting) */
  111. if (!output_pos && !omitted)
  112. sprintf(dumpdata, " %08x%*s\r\n", p-(p%16), 1+3*16+2+16, "");
  113. /* Deal with the current byte. */
  114. if (blktype == PKTLOG_OMIT) {
  115. omitted++;
  116. } else {
  117. int c;
  118. if (blktype == PKTLOG_BLANK) {
  119. c = 'X';
  120. sprintf(smalldata, "XX");
  121. } else { /* PKTLOG_EMIT */
  122. c = ((unsigned char *)data)[p];
  123. sprintf(smalldata, "%02x", c);
  124. }
  125. dumpdata[10+2+3*(p%16)] = smalldata[0];
  126. dumpdata[10+2+3*(p%16)+1] = smalldata[1];
  127. dumpdata[10+1+3*16+2+(p%16)] = (isprint(c) ? c : '.');
  128. output_pos = (p%16) + 1;
  129. }
  130. p++;
  131. /* Flush row if necessary */
  132. if (((p % 16) == 0) || (p == len) || omitted) {
  133. if (output_pos) {
  134. strcpy(dumpdata + 10+1+3*16+2+output_pos, "\r\n");
  135. fputs(dumpdata, ctx->lgfp);
  136. output_pos = 0;
  137. }
  138. }
  139. }
  140. /* Tidy up */
  141. if (omitted)
  142. fprintf(ctx->lgfp, " (%d byte%s omitted)\r\n",
  143. omitted, (omitted==1?"":"s"));
  144. fflush(ctx->lgfp);
  145. }
  146. }
  147. /* open log file append/overwrite mode */
  148. void logfopen(void *handle)
  149. {
  150. struct LogContext *ctx = (struct LogContext *)handle;
  151. char buf[256];
  152. time_t t;
  153. struct tm tm;
  154. char writemod[4];
  155. /* Prevent repeat calls */
  156. if (ctx->lgfp)
  157. return;
  158. if (!ctx->cfg.logtype)
  159. return;
  160. sprintf(writemod, "wb"); /* default to rewrite */
  161. time(&t);
  162. tm = *localtime(&t);
  163. /* substitute special codes in file name */
  164. xlatlognam(&ctx->currlogfilename, ctx->cfg.logfilename,ctx->cfg.host, &tm);
  165. ctx->lgfp = f_open(ctx->currlogfilename, "r"); /* file already present? */
  166. if (ctx->lgfp) {
  167. int i;
  168. fclose(ctx->lgfp);
  169. if (ctx->cfg.logxfovr != LGXF_ASK) {
  170. i = ((ctx->cfg.logxfovr == LGXF_OVR) ? 2 : 1);
  171. } else
  172. i = askappend(ctx->frontend, ctx->currlogfilename);
  173. if (i == 1)
  174. writemod[0] = 'a'; /* set append mode */
  175. else if (i == 0) { /* cancelled */
  176. ctx->lgfp = NULL;
  177. ctx->cfg.logtype = 0; /* disable logging */
  178. return;
  179. }
  180. }
  181. ctx->lgfp = f_open(ctx->currlogfilename, writemod);
  182. if (ctx->lgfp) { /* enter into event log */
  183. /* --- write header line into log file */
  184. fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", ctx->lgfp);
  185. strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
  186. fputs(buf, ctx->lgfp);
  187. fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", ctx->lgfp);
  188. sprintf(buf, "%s session log (%s mode) to file: ",
  189. (writemod[0] == 'a') ? "Appending" : "Writing new",
  190. (ctx->cfg.logtype == LGTYP_ASCII ? "ASCII" :
  191. ctx->cfg.logtype == LGTYP_DEBUG ? "raw" :
  192. ctx->cfg.logtype == LGTYP_PACKETS ? "SSH packets" : "<ukwn>"));
  193. /* Make sure we do not exceed the output buffer size */
  194. strncat(buf, filename_to_str(&ctx->currlogfilename), 128);
  195. buf[strlen(buf)] = '\0';
  196. logevent(ctx->frontend, buf);
  197. }
  198. }
  199. void logfclose(void *handle)
  200. {
  201. struct LogContext *ctx = (struct LogContext *)handle;
  202. if (ctx->lgfp) {
  203. fclose(ctx->lgfp);
  204. ctx->lgfp = NULL;
  205. }
  206. }
  207. void *log_init(void *frontend, Config *cfg)
  208. {
  209. struct LogContext *ctx = snew(struct LogContext);
  210. ctx->lgfp = NULL;
  211. ctx->frontend = frontend;
  212. ctx->cfg = *cfg; /* STRUCTURE COPY */
  213. return ctx;
  214. }
  215. void log_free(void *handle)
  216. {
  217. struct LogContext *ctx = (struct LogContext *)handle;
  218. logfclose(ctx);
  219. sfree(ctx);
  220. }
  221. void log_reconfig(void *handle, Config *cfg)
  222. {
  223. struct LogContext *ctx = (struct LogContext *)handle;
  224. int reset_logging;
  225. if (!filename_equal(ctx->cfg.logfilename, cfg->logfilename) ||
  226. ctx->cfg.logtype != cfg->logtype)
  227. reset_logging = TRUE;
  228. else
  229. reset_logging = FALSE;
  230. if (reset_logging)
  231. logfclose(ctx);
  232. ctx->cfg = *cfg; /* STRUCTURE COPY */
  233. if (reset_logging)
  234. logfopen(ctx);
  235. }
  236. /*
  237. * translate format codes into time/date strings
  238. * and insert them into log file name
  239. *
  240. * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h":<hostname> "&&":&
  241. */
  242. static void xlatlognam(Filename *dest, Filename src,
  243. char *hostname, struct tm *tm) {
  244. char buf[10], *bufp;
  245. int size;
  246. char buffer[FILENAME_MAX];
  247. int len = sizeof(buffer)-1;
  248. char *d;
  249. const char *s;
  250. d = buffer;
  251. s = filename_to_str(&src);
  252. while (*s) {
  253. /* Let (bufp, len) be the string to append. */
  254. bufp = buf; /* don't usually override this */
  255. if (*s == '&') {
  256. char c;
  257. s++;
  258. size = 0;
  259. if (*s) switch (c = *s++, tolower(c)) {
  260. case 'y':
  261. size = strftime(buf, sizeof(buf), "%Y", tm);
  262. break;
  263. case 'm':
  264. size = strftime(buf, sizeof(buf), "%m", tm);
  265. break;
  266. case 'd':
  267. size = strftime(buf, sizeof(buf), "%d", tm);
  268. break;
  269. case 't':
  270. size = strftime(buf, sizeof(buf), "%H%M%S", tm);
  271. break;
  272. case 'h':
  273. bufp = hostname;
  274. size = strlen(bufp);
  275. break;
  276. default:
  277. buf[0] = '&';
  278. size = 1;
  279. if (c != '&')
  280. buf[size++] = c;
  281. }
  282. } else {
  283. buf[0] = *s++;
  284. size = 1;
  285. }
  286. if (size > len)
  287. size = len;
  288. memcpy(d, bufp, size);
  289. d += size;
  290. len -= size;
  291. }
  292. *d = '\0';
  293. *dest = filename_from_str(buffer);
  294. }