+ struct LogContext *ctx = (struct LogContext *)handle;
+ if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) {
+ fprintf(stderr, "%s\n", event);
+ fflush(stderr);
+ }
+ /* If we don't have a context yet (eg winnet.c init) then skip entirely */
+ if (!ctx)
+ return;
+ if (ctx->logtype != LGTYP_PACKETS &&
+ ctx->logtype != LGTYP_SSHRAW)
+ return;
+ logprintf(ctx, "Event Log: %s\r\n", event);
+ logflush(ctx);
+}
+
+/*
+ * Log an SSH packet.
+ * If n_blanks != 0, blank or omit some parts.
+ * Set of blanking areas must be in increasing order.
+ */
+void log_packet(void *handle, int direction, int type,
+ const char *texttype, const void *data, int len,
+ int n_blanks, const struct logblank_t *blanks,
+ const unsigned long *seq,
+ unsigned downstream_id, const char *additional_log_text)
+{
+ struct LogContext *ctx = (struct LogContext *)handle;
+ char dumpdata[80], smalldata[5];
+ int p = 0, b = 0, omitted = 0;
+ int output_pos = 0; /* NZ if pending output in dumpdata */
+
+ if (!(ctx->logtype == LGTYP_SSHRAW ||
+ (ctx->logtype == LGTYP_PACKETS && texttype)))
+ return;
+
+ /* Packet header. */
+ if (texttype) {
+ logprintf(ctx, "%s packet ",
+ direction == PKT_INCOMING ? "Incoming" : "Outgoing");
+
+ if (seq)
+ logprintf(ctx, "#0x%lx, ", *seq);
+
+ logprintf(ctx, "type %d / 0x%02x (%s)", type, type, texttype);
+
+ if (downstream_id) {
+ logprintf(ctx, " on behalf of downstream #%u", downstream_id);
+ if (additional_log_text)
+ logprintf(ctx, " (%s)", additional_log_text);
+ }
+
+ logprintf(ctx, "\r\n");
+ } else {
+ /*
+ * Raw data is logged with a timestamp, so that it's possible
+ * to determine whether a mysterious delay occurred at the
+ * client or server end. (Timestamping the raw data avoids
+ * cluttering the normal case of only logging decrypted SSH
+ * messages, and also adds conceptual rigour in the case where
+ * an SSH message arrives in several pieces.)
+ */
+ char buf[256];
+ struct tm tm;
+ tm = ltime();
+ strftime(buf, 24, "%Y-%m-%d %H:%M:%S", &tm);
+ logprintf(ctx, "%s raw data at %s\r\n",
+ direction == PKT_INCOMING ? "Incoming" : "Outgoing",
+ buf);
+ }
+
+ /*
+ * Output a hex/ASCII dump of the packet body, blanking/omitting
+ * parts as specified.
+ */
+ while (p < len) {
+ int blktype;
+
+ /* Move to a current entry in the blanking array. */
+ while ((b < n_blanks) &&
+ (p >= blanks[b].offset + blanks[b].len))
+ b++;
+ /* Work out what type of blanking to apply to
+ * this byte. */
+ blktype = PKTLOG_EMIT; /* default */
+ if ((b < n_blanks) &&
+ (p >= blanks[b].offset) &&
+ (p < blanks[b].offset + blanks[b].len))
+ blktype = blanks[b].type;
+
+ /* If we're about to stop omitting, it's time to say how
+ * much we omitted. */
+ if ((blktype != PKTLOG_OMIT) && omitted) {
+ logprintf(ctx, " (%d byte%s omitted)\r\n",
+ omitted, (omitted==1?"":"s"));
+ omitted = 0;
+ }
+
+ /* (Re-)initialise dumpdata as necessary
+ * (start of row, or if we've just stopped omitting) */
+ if (!output_pos && !omitted)
+ sprintf(dumpdata, " %08x%*s\r\n", p-(p%16), 1+3*16+2+16, "");
+
+ /* Deal with the current byte. */
+ if (blktype == PKTLOG_OMIT) {
+ omitted++;
+ } else {
+ int c;
+ if (blktype == PKTLOG_BLANK) {
+ c = 'X';
+ sprintf(smalldata, "XX");
+ } else { /* PKTLOG_EMIT */
+ c = ((unsigned char *)data)[p];
+ sprintf(smalldata, "%02x", c);
+ }
+ dumpdata[10+2+3*(p%16)] = smalldata[0];
+ dumpdata[10+2+3*(p%16)+1] = smalldata[1];
+ dumpdata[10+1+3*16+2+(p%16)] = (isprint(c) ? c : '.');
+ output_pos = (p%16) + 1;
+ }
+
+ p++;
+
+ /* Flush row if necessary */
+ if (((p % 16) == 0) || (p == len) || omitted) {
+ if (output_pos) {
+ strcpy(dumpdata + 10+1+3*16+2+output_pos, "\r\n");
+ logwrite(ctx, dumpdata, strlen(dumpdata));
+ output_pos = 0;
+ }
+ }
+
+ }
+
+ /* Tidy up */
+ if (omitted)
+ logprintf(ctx, " (%d byte%s omitted)\r\n",
+ omitted, (omitted==1?"":"s"));
+ logflush(ctx);
+}
+
+void *log_init(void *frontend, Conf *conf)
+{
+ struct LogContext *ctx = snew(struct LogContext);