diff --git a/log.c b/log.c index cd12bb9..6d566f4 100644 --- a/log.c +++ b/log.c @@ -262,12 +262,13 @@ log_content_close_singlefile(void) * Generate a log path based on the given log spec. * Returns an allocated buffer which must be freed by caller, or NULL on error. */ -static char * +#define PATH_BUF_INC 1024 +static char * WUNRES NONNULL(1,2,3) log_content_format_pathspec(const char *logspec, char *srcaddr, char *dstaddr, - char *exec_path, char *user, char *group) + char *exec_path, char *user, char *group) { /* set up buffer to hold our generated file path */ - size_t path_buflen = 1024; + size_t path_buflen = PATH_BUF_INC; char *path_buf = malloc(path_buflen); if (path_buf == NULL) { log_err_printf("failed to allocate path buffer\n"); @@ -284,79 +285,81 @@ log_content_format_pathspec(const char *logspec, char *srcaddr, char *dstaddr, size_t elem_len = 0; const char iso8601[] = "%Y%m%dT%H%M%SZ"; - char timebuf[24]; // sized for ISO 8601 format + char timebuf[24]; /* sized for ISO 8601 format */ /* parse the format string and generate the next path element */ switch (*p) { + case '%': + p++; + /* handle format specifiers. */ + switch (*p) { + case '\0': + /* unexpected eof; backtrack and discard + * invalid format spec */ + p--; + elem_len = 0; + break; case '%': - p++; - /* handle format specifiers. */ - switch (*p) { - case '\0': - /* unexpected eof; backtrack and discard invalid format spec */ - p--; - elem_len = 0; - break; - case '%': - elem = p; - elem_len = 1; - break; - case 'd': - elem = dstaddr; - elem_len = strlen(dstaddr); - break; - case 's': - elem = srcaddr; - elem_len = strlen(srcaddr); - break; - case 'x': - if (exec_path) { - char *match = exec_path; - while ((match = strchr(match, '/')) != NULL) { - match++; - elem = match; - } - elem_len = strlen(elem); - } else { - elem_len = 0; - } - break; - case 'X': - elem = exec_path; - elem_len = exec_path ? strlen(exec_path) : 0; - break; - case 'u': - elem = user; - elem_len = user ? strlen(user) : 0; - break; - case 'g': - elem = group; - elem_len = group ? strlen(group) : 0; - break; - case 'T': { - time_t epoch; - struct tm *utc; - - time(&epoch); - utc = gmtime(&epoch); - strftime(timebuf, sizeof(timebuf), iso8601, utc); - - elem = timebuf; - elem_len = sizeof(timebuf); - break; + elem = p; + elem_len = 1; + break; + case 'd': + elem = dstaddr; + elem_len = strlen(dstaddr); + break; + case 's': + elem = srcaddr; + elem_len = strlen(srcaddr); + break; + case 'x': + if (exec_path) { + char *match = exec_path; + while ((match = strchr(match, '/')) != NULL) { + match++; + elem = match; } + elem_len = strlen(elem); + } else { + elem_len = 0; } break; - default: - elem = p; - elem_len = 1; + case 'X': + elem = exec_path; + elem_len = exec_path ? strlen(exec_path) : 0; + break; + case 'u': + elem = user; + elem_len = user ? strlen(user) : 0; break; + case 'g': + elem = group; + elem_len = group ? strlen(group) : 0; + break; + case 'T': { + time_t epoch; + struct tm *utc; + + time(&epoch); + utc = gmtime(&epoch); + strftime(timebuf, sizeof(timebuf), iso8601, utc); + + elem = timebuf; + elem_len = sizeof(timebuf); + break; + }} + break; + default: + elem = p; + elem_len = 1; + break; } /* growing the buffer to fit elem_len + terminating \0 */ if (path_buflen - path_len < elem_len + 1) { - /* grow in 1024 chunks. note that the use of `1024' provides our gauranteed space for a trailing '\0' */ - path_buflen += elem_len + 1024; + /* Grow in PATH_BUF_INC chunks. + * Note that the use of `PATH_BUF_INC' provides our + * gauranteed space for a trailing '\0' */ + path_buflen += elem_len + PATH_BUF_INC; char *newbuf = realloc(path_buf, path_buflen); if (newbuf == NULL) { log_err_printf("failed to reallocate path buffer"); @@ -375,6 +378,7 @@ log_content_format_pathspec(const char *logspec, char *srcaddr, char *dstaddr, path_buf[path_len] = '\0'; return path_buf; } +#undef PATH_BUF_INC void log_content_open(log_content_ctx_t *ctx, char *srcaddr, char *dstaddr, @@ -384,19 +388,20 @@ log_content_open(log_content_ctx_t *ctx, char *srcaddr, char *dstaddr, return; if (content_fd != -1) { + /* single-file content log (-L) */ ctx->fd = content_fd; asprintf(&ctx->header_in, "%s -> %s", srcaddr, dstaddr); asprintf(&ctx->header_out, "%s -> %s", dstaddr, srcaddr); } else if (content_logspec) { + /* per-connection-file content log with logspec (-F) */ char *filename; filename = log_content_format_pathspec(content_logspec, - srcaddr, dstaddr, - exec_path, user, - group); + srcaddr, dstaddr, + exec_path, user, group); /* statefully create parent directories by iteratively rewriting - * the path at each directory seperator */ + * the path at each directory seperator */ char parent[strlen(filename)+1]; char *p; @@ -407,20 +412,23 @@ log_content_open(log_content_ctx_t *ctx, char *srcaddr, char *dstaddr, while (*p == '/') p++; while ((p = strchr(p, '/')) != NULL) { - /* overwrite '/' to terminate the string at the next parent directory */ + /* overwrite '/' to terminate the string at the next + * parent directory */ *p = '\0'; struct stat sbuf; if (stat(parent, &sbuf) != 0) { if (mkdir(parent, 0755) != 0) { - log_err_printf("Could not create directory '%s': %s\n", + log_err_printf("Could not create " + "directory '%s': %s\n", parent, strerror(errno)); ctx->fd = -1; return; } } else if (!S_ISDIR(sbuf.st_mode)) { - log_err_printf("Failed to open '%s': %s is not a directory\n", - filename, parent); + log_err_printf("Failed to open '%s': " + "%s is not a directory\n", + filename, parent); ctx->fd = -1; return; } @@ -433,13 +441,13 @@ log_content_open(log_content_ctx_t *ctx, char *srcaddr, char *dstaddr, while (*p == '/') p++; } - ctx->fd = open(filename, O_WRONLY|O_APPEND|O_CREAT, 0660); - if (ctx->fd == -1) { - log_err_printf("Failed to open '%s': %s\n", - filename, strerror(errno)); - } - + ctx->fd = open(filename, O_WRONLY|O_APPEND|O_CREAT, 0660); + if (ctx->fd == -1) { + log_err_printf("Failed to open '%s': %s\n", + filename, strerror(errno)); + } } else { + /* per-connection-file content log (-S) */ char filename[1024]; char timebuf[24]; time_t epoch; @@ -571,8 +579,7 @@ log_preinit(opts_t *opts) if (log_content_open_logspec(opts->contentlog) == -1) goto out; } else { - if (log_content_open_singlefile(opts->contentlog) - == -1) + if (log_content_open_singlefile(opts->contentlog) == -1) goto out; } if (!(content_log = logger_new(log_content_writecb))) { diff --git a/main.c b/main.c index 7f58605..40310c9 100644 --- a/main.c +++ b/main.c @@ -138,28 +138,24 @@ main_usage(void) " -j jaildir chroot() to jaildir (impacts -S and sni, see manual page)\n" " -p pidfile write pid to pidfile (default: no pid file)\n" " -l logfile connect log: log one line summary per connection to logfile\n" -" -L logfile content log: full data to file or named pipe (excludes -S)\n" +" -L logfile content log: full data to file or named pipe (excludes -S/-F)\n" " -S logdir content log: full data to separate files in dir (excludes -L/-F)\n" -" -F pathspec content log: full data to separate files with the given path spec (excludes -L/-S):\n" -" pathspec = \n" -" The following directives are supported.\n" +" -F pathspec content log: full data to sep files with %% subst (excl. -L/-S):\n" +" %%T - initial connection time as an ISO 8601 UTC timestamp\n" " %%d - dest address:port\n" " %%s - source address:port\n" -" %%x - base name of local process. if unavailable, will be skipped.\n" -" %%X - full path to local process; . if unavailable, will be skipped.\n" -" %%u - username or uid of local process. if unavailable, will be skipped\n" -" %%g - group or gid of local process. if unavailable, will be skipped\n" -" %%T - initial connection time as an ISO 8601 UTC timestamp\n" +" %%x - base name of local process (skipped if unavailable)\n" +" %%X - full path to local process (skipped if unavailable)\n" +" %%u - user name or id of local process (skipped if unavailable)\n" +" %%g - group name or id of local process (skipped if unavailable)\n" " %%%% - literal '%%'\n" -" e.g.\n" -" \"/var/log/sslsplit/%%X/%%u-%%s-%%d-%%T\"\n" -" Unknown directives are ignored, and intermediate directories will be created automatically\n" +" e.g. \"/var/log/sslsplit/%%X/%%u-%%s-%%d-%%T\"\n" " -d daemon mode: run in background, log error messages to syslog\n" " -D debug mode: run in foreground, log debug messages on stderr\n" " -V print version information and exit\n" " -h print usage information and exit\n" " proxyspec = type listenaddr+port [natengine|targetaddr+port|\"sni\"+port]\n" -" e.g. http 0.0.0.0 8080 www.roe.ch 80 # http/4; static hostname dst\n" +" e.g. http 0.0.0.0 8080 www.roe.ch 80 # http/4; static hostname dst\n" " https ::1 8443 2001:db8::1 443 # https/6; static address dst\n" " https 127.0.0.1 9443 sni 443 # https/4; SNI DNS lookups\n" " tcp 127.0.0.1 10025 # tcp/4; default NAT engine\n" @@ -504,8 +500,8 @@ main(int argc, char *argv[]) opts->contentlog = strdup(optarg); if (!opts->contentlog) oom_die(argv0); - opts->contentlogspec = 1; opts->contentlogdir = 0; + opts->contentlogspec = 1; break; case 'd': opts->detach = 1;