Bug Summary

File:log/nslog.c
Warning:line 716, column 13
Duplicate code detected
Note:line 719, column 13
Similar code here

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name nslog.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/isvv/naviserver/nslog -resource-dir /usr/local/lib/clang/15.0.0 -D _FORTIFY_SOURCE=2 -D NDEBUG -D SYSTEM_MALLOC -I ../include -I /usr/include/tcl8.6 -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=c99 -fdebug-compilation-dir=/home/isvv/naviserver/nslog -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-checker alpha -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-07-23-130959-11103-1 -x c nslog.c
1/*
2 * The contents of this file are subject to the Mozilla Public License
3 * Version 1.1(the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://www.mozilla.org/.
6 *
7 * Software distributed under the License is distributed on an "AS IS"
8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9 * the License for the specific language governing rights and limitations
10 * under the License.
11 *
12 * The Original Code is AOLserver Code and related documentation
13 * distributed by AOL.
14 *
15 * The Initial Developer of the Original Code is America Online,
16 * Inc. Portions created by AOL are Copyright(C) 1999 America Online,
17 * Inc. All Rights Reserved.
18 *
19 * Alternatively, the contents of this file may be used under the terms
20 * of the GNU General Public License(the "GPL"), in which case the
21 * provisions of GPL are applicable instead of those above. If you wish
22 * to allow use of your version of this file only under the terms of the
23 * GPL and not to allow others to use your version of this file under the
24 * License, indicate your decision by deleting the provisions above and
25 * replace them with the notice and other provisions required by the GPL.
26 * If you do not delete the provisions above, a recipient may use your
27 * version of this file under either the License or the GPL.
28 *
29 */
30
31/*
32 * nslog.c --
33 *
34 * Implements access logging in the NCSA Common Log format.
35 *
36 */
37
38#include "ns.h"
39#include <ctype.h> /* isspace */
40
41#define LOG_COMBINED0x01u 0x01u
42#define LOG_FMTTIME0x02u 0x02u
43#define LOG_REQTIME0x04u 0x04u
44#define LOG_PARTIALTIMES0x08u 0x08u
45#define LOG_CHECKFORPROXY0x10u 0x10u
46#define LOG_SUPPRESSQUERY0x20u 0x20u
47#define LOG_THREADNAME0x40u 0x40u
48#define LOG_MASKIP0x80u 0x80u
49
50#if !defined(PIPE_BUF4096)
51# define PIPE_BUF4096 512
52#endif
53
54NS_EXTERNextern __attribute__ ((visibility ("default"))) const int Ns_ModuleVersion;
55NS_EXPORT__attribute__ ((visibility ("default"))) const int Ns_ModuleVersion = 1;
56
57
58typedef struct {
59 Ns_Mutex lock;
60 const char *module;
61 const char *filename;
62 const char *rollfmt;
63 const char *extendedHeaders;
64 const char **requestHeaders;
65 const char **responseHeaders;
66 const char *driverPattern;
67 int nrRequestHeaders;
68 int nrResponseHeaders;
69 int fd;
70 unsigned int flags;
71 int maxbackup;
72 int maxlines;
73 int curlines;
74 struct NS_SOCKADDR_STORAGEsockaddr_storage ipv4maskStruct;
75 struct sockaddr *ipv4maskPtr;
76#ifdef HAVE_IPV61
77 struct NS_SOCKADDR_STORAGEsockaddr_storage ipv6maskStruct;
78 struct sockaddr *ipv6maskPtr;
79#endif
80 Tcl_DString buffer;
81} Log;
82
83/*
84 * Local functions defined in this file
85 */
86
87static Ns_SchedProc LogRollCallback;
88static Ns_ShutdownProc LogCloseCallback;
89static Ns_TraceProc LogTrace;
90static Ns_ArgProc LogArg;
91static Ns_TclTraceProc AddCmds;
92static Tcl_ObjCmdProc LogObjCmd;
93
94NS_EXPORT__attribute__ ((visibility ("default"))) Ns_ModuleInitProc Ns_ModuleInit;
95
96static Ns_ReturnCode LogFlush(Log *logPtr, Tcl_DString *dsPtr);
97static Ns_LogCallbackProc LogOpen;
98static Ns_LogCallbackProc LogClose;
99static Ns_LogCallbackProc LogRoll;
100
101static void AppendEscaped(Tcl_DString *dsPtr, const char *toProcess)
102 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
103
104static Ns_ReturnCode ParseExtendedHeaders(Log *logPtr, const char *str)
105 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
106static void
107AppendExtHeaders(Tcl_DString *dsPtr, const char **argv, const Ns_Set *set)
108 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
109
110
111/*
112 *----------------------------------------------------------------------
113 *
114 * Ns_ModuleInit --
115 *
116 * Module initialization routine.
117 *
118 * Results:
119 * NS_OK.
120 *
121 * Side effects:
122 * Log file is opened, trace routine is registered, and, if
123 * configured, log file roll signal and scheduled procedures
124 * are registered.
125 *
126 *----------------------------------------------------------------------
127 */
128
129NS_EXPORT__attribute__ ((visibility ("default"))) Ns_ReturnCode
130Ns_ModuleInit(const char *server, const char *module)
131{
132 const char *path, *file;
133 Log *logPtr;
134 Tcl_DString ds;
135 static bool_Bool first = NS_TRUE1;
136 Ns_ReturnCode result;
137
138 NS_NONNULL_ASSERT(module != NULL)((void) (0));
139
140 /*
141 * Sanity check to provide a meaningful error instead of a
142 * crash. Currently, we do not allow one to register this module globally.
143 */
144 if (server == NULL((void*)0)) {
145 Ns_Fatal("Module %s: requires a concrete server (cannot be used as a global module)",
146 module);
147 }
148
149 /*
150 * Register the info callbacks just once. This assumes we are
151 * called w/o locking from within the server startup.
152 */
153
154 if (first) {
155 first = NS_FALSE0;
156 Ns_RegisterProcInfo((ns_funcptr_t)LogRollCallback, "nslog:roll", LogArg);
157 Ns_RegisterProcInfo((ns_funcptr_t)LogCloseCallback, "nslog:close", LogArg);
158 Ns_RegisterProcInfo((ns_funcptr_t)LogTrace, "nslog:conntrace", LogArg);
159 Ns_RegisterProcInfo((ns_funcptr_t)AddCmds, "nslog:initinterp", LogArg);
160 }
161
162 Tcl_DStringInit(&ds);
163
164 logPtr = ns_calloc(1u, sizeof(Log));
165 logPtr->module = module;
166 logPtr->fd = NS_INVALID_FD(-1);
167 Ns_MutexInit(&logPtr->lock);
168 Ns_MutexSetName2(&logPtr->lock, "nslog", server);
169 Tcl_DStringInit(&logPtr->buffer);
170
171 path = Ns_ConfigSectionPath(NULL((void*)0), server, module, (char *)0L);
172
173 /*
174 * Determine the name of the log file
175 */
176
177 file = Ns_ConfigString(path, "file", "access.log");
178 if (Ns_PathIsAbsolute(file) == NS_TRUE1) {
179 logPtr->filename = ns_strdup(file);
180 } else {
181 /*
182 * If log file is not given in absolute format, it is expected to
183 * exist in the global logs directory if such exists or module
184 * specific directory, which is created if necessary.
185 */
186
187 if (Ns_HomePathExists("logs", (char *)0L)) {
188 (void) Ns_HomePath(&ds, "logs", "/", file, (char *)0L);
189 } else {
190 Tcl_Obj *dirpath;
191 int rc;
192
193 Tcl_DStringSetLength(&ds, 0);
194 (void) Ns_ModulePath(&ds, server, module, (char *)0L);
195 dirpath = Tcl_NewStringObj(ds.string, -1);
196 Tcl_IncrRefCount(dirpath)++(dirpath)->refCount;
197 rc = Tcl_FSCreateDirectory(dirpath);
198 Tcl_DecrRefCount(dirpath)do { Tcl_Obj *_objPtr = (dirpath); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
199 if (rc != TCL_OK0 && Tcl_GetErrno() != EEXIST17 && Tcl_GetErrno() != EISDIR21) {
200 Ns_Log(Error, "nslog: create directory (%s) failed: '%s'",
201 ds.string, strerror(Tcl_GetErrno()));
202 Tcl_DStringFree(&ds);
203 return NS_ERROR;
204 }
205 Tcl_DStringSetLength(&ds, 0);
206 (void) Ns_ModulePath(&ds, server, module, file, (char *)0L);
207 }
208 logPtr->filename = Ns_DStringExport(&ds);
209 }
210
211 /*
212 * Get other parameters from configuration file
213 */
214
215 logPtr->rollfmt = ns_strcopy(Ns_ConfigGetValue(path, "rollfmt"));
216 logPtr->maxbackup = Ns_ConfigIntRange(path, "maxbackup", 100, 1, INT_MAX2147483647);
217 logPtr->maxlines = Ns_ConfigIntRange(path, "maxbuffer", 0, 0, INT_MAX2147483647);
218 if (Ns_ConfigBool(path, "formattedtime", NS_TRUE1)) {
219 logPtr->flags |= LOG_FMTTIME0x02u;
220 }
221 if (Ns_ConfigBool(path, "logcombined", NS_TRUE1)) {
222 logPtr->flags |= LOG_COMBINED0x01u;
223 }
224 if (Ns_ConfigBool(path, "logreqtime", NS_FALSE0)) {
225 logPtr->flags |= LOG_REQTIME0x04u;
226 }
227 if (Ns_ConfigBool(path, "logpartialtimes", NS_FALSE0)) {
228 logPtr->flags |= LOG_PARTIALTIMES0x08u;
229 }
230 if (Ns_ConfigBool(path, "logthreadname", NS_FALSE0)) {
231 logPtr->flags |= LOG_THREADNAME0x40u;
232 }
233 if (Ns_ConfigBool(path, "suppressquery", NS_FALSE0)) {
234 logPtr->flags |= LOG_SUPPRESSQUERY0x20u;
235 }
236 if (Ns_ConfigBool(path, "checkforproxy", NS_FALSE0)) {
237 Ns_Log(Warning, "parameter checkforproxy of module nslog is deprecated; "
238 "use global parameter reversproxymode instead");
239 logPtr->flags |= LOG_CHECKFORPROXY0x10u;
240 }
241
242 logPtr->driverPattern = Ns_ConfigString(path, "driver", NULL((void*)0));
243
244 logPtr->ipv4maskPtr = NULL((void*)0);
245#ifdef HAVE_IPV61
246 logPtr->ipv6maskPtr = NULL((void*)0);
247#endif
248 if (Ns_ConfigBool(path, "masklogaddr", NS_FALSE0)) {
249 const char* maskString;
250 const char *default_ipv4MaskString = "255.255.255.0";
251#ifdef HAVE_IPV61
252 const char *default_ipv6MaskString = "ff:ff:ff:ff::";
253#endif
254 logPtr->flags |= LOG_MASKIP0x80u;
255
256#ifdef HAVE_IPV61
257 maskString = Ns_ConfigGetValue(path, "maskipv6");
258 if (maskString == NULL((void*)0)) {
259 maskString = default_ipv6MaskString;
260 }
261
262 if (ns_inet_pton((struct sockaddr *)&logPtr->ipv6maskStruct, maskString) == 1) {
263 logPtr->ipv6maskPtr = (struct sockaddr *)&logPtr->ipv6maskStruct;
264 }
265#endif
266 maskString = Ns_ConfigGetValue(path, "maskipv4");
267 if (maskString == NULL((void*)0)) {
268 maskString = default_ipv4MaskString;
269 }
270 if (ns_inet_pton((struct sockaddr *)&logPtr->ipv4maskStruct, maskString) == 1) {
271 logPtr->ipv4maskPtr = (struct sockaddr *)&logPtr->ipv4maskStruct;
272 }
273 }
274
275 /*
276 * Schedule various log roll and shutdown options.
277 */
278
279 if (Ns_ConfigBool(path, "rolllog", NS_TRUE1)) {
280 int hour = Ns_ConfigIntRange(path, "rollhour", 0, 0, 23);
281
282 Ns_ScheduleDaily(LogRollCallback, logPtr,
283 0, hour, 0, NULL((void*)0));
284 }
285 if (Ns_ConfigBool(path, "rollonsignal", NS_FALSE0)) {
286 Ns_RegisterAtSignal((Ns_Callback *)(ns_funcptr_t)LogRollCallback, logPtr);
287 }
288
289 /*
290 * Parse extended headers; it is just a list of names
291 */
292 (void)ParseExtendedHeaders(logPtr, Ns_ConfigGetValue(path, "extendedheaders"));
293
294 /*
295 * Open the log and register the trace
296 */
297
298 if (LogOpen(logPtr) != NS_OK) {
299 return NS_ERROR;
300 }
301
302 Ns_RegisterServerTrace(server, LogTrace, logPtr);
303 Ns_RegisterAtShutdown(LogCloseCallback, logPtr);
304 result = Ns_TclRegisterTrace(server, AddCmds, logPtr, NS_TCL_TRACE_CREATE);
305
306 return result;
307}
308
309static Ns_ReturnCode
310AddCmds(Tcl_Interp *interp, const void *arg)
311{
312 const Log *logPtr = arg;
313
314 Tcl_CreateObjCommand(interp, "ns_accesslog", LogObjCmd, (ClientData)logPtr, NULL((void*)0));
315 return NS_OK;
316}
317
318/*
319 *----------------------------------------------------------------------
320 *
321 * ParseExtendedHeaders --
322 *
323 * Parse a string specifying the extended parameters.
324 * The string might be:
325 *
326 * - a Tcl list of plain request header fields, like e.g.
327 * {Referer X-Forwarded-For}
328 *
329 * - a Tcl list of header fields with tags to denote request or response
330 * header fields, like e.g. {req:Referer response:Content-Type}
331 *
332 * Results:
333 * None
334 *
335 * Side effects:
336 * Upadating fields in logPtr
337 *
338 *----------------------------------------------------------------------
339 */
340static Ns_ReturnCode
341ParseExtendedHeaders(Log *logPtr, const char *str)
342{
343 Ns_ReturnCode result = NS_OK;
344
345 NS_NONNULL_ASSERT(logPtr != NULL)((void) (0));
346
347 if (str != NULL((void*)0)) {
348 int argc;
349 const char **argv;
350
351 if (Tcl_SplitList(NULL((void*)0), str, &argc, &argv) != TCL_OK0) {
352 Ns_Log(Error, "nslog: invalid 'extendedHeaders' parameter: '%s'", str);
353 result = NS_ERROR;
354
355 } else {
356 int i, tagged = 0;
357
358 if (logPtr->extendedHeaders != NULL((void*)0)) {
359 ns_free((char *)logPtr->extendedHeaders);
360 }
361 if (logPtr->requestHeaders != NULL((void*)0)) {
362 Tcl_Free((char *) logPtr->requestHeaders);
363 }
364 if (logPtr->responseHeaders != NULL((void*)0)) {
365 Tcl_Free((char *)logPtr->responseHeaders);
366 }
367 logPtr->extendedHeaders = ns_strdup(str);
368
369 for (i = 0; i < argc; i++) {
370 const char *fieldName = argv[i];
371
372 if (strchr(fieldName, ':') != NULL((void*)0)) {
373 tagged ++;
374 }
375 }
376 if (tagged == 0) {
377 logPtr->requestHeaders = (const char **)argv;
378 logPtr->nrRequestHeaders = argc;
379 logPtr->responseHeaders = NULL((void*)0);
380 logPtr->nrResponseHeaders = 0;
381 } else {
382 Tcl_DString requestHeaderFields, responseHeaderFields;
383 int nrRequestsHeaderFields = 0, nrResponseHeaderFields = 0;
384
385 Tcl_DStringInit(&requestHeaderFields);
386 Tcl_DStringInit(&responseHeaderFields);
387
388 for (i = 0; i < argc; i++) {
389 const char *fieldName = argv[i];
390 char *suffix = strchr(fieldName, ':');
391
392 if (suffix != NULL((void*)0)) {
393 *suffix = '\0';
394 suffix ++;
395 if (strncmp(fieldName, "request", 3) == 0) {
396 Tcl_DStringAppendElement(&requestHeaderFields, suffix);
397 nrRequestsHeaderFields++;
398 } else if (strncmp(fieldName, "response", 3) == 0) {
399 Tcl_DStringAppendElement(&responseHeaderFields, suffix);
400 nrResponseHeaderFields++;
401 } else {
402 Ns_Log(Error, "nslog: ignore invalid entry prefix '%s' in extendedHeaders parameter",
403 fieldName);
404 }
405 } else {
406 /*
407 * No prefix, assume request header field
408 */
409 Tcl_DStringAppendElement(&requestHeaderFields, suffix);
410 nrRequestsHeaderFields++;
411 }
412 }
413 (void) Tcl_SplitList(NULL((void*)0), requestHeaderFields.string,
414 &logPtr->nrRequestHeaders,
415 &logPtr->requestHeaders);
416 (void) Tcl_SplitList(NULL((void*)0), responseHeaderFields.string,
417 &logPtr->nrResponseHeaders,
418 &logPtr->responseHeaders);
419
420 Tcl_DStringFree(&requestHeaderFields);
421 Tcl_DStringFree(&responseHeaderFields);
422 Tcl_Free((char*)argv);
423 }
424 }
425 }
426 return result;
427}
428
429/*
430 *----------------------------------------------------------------------
431 *
432 * LogObjCmd --
433 *
434 * Implements "ns_accesslog".
435 *
436 * Results:
437 * Standard Tcl result.
438 *
439 * Side effects:
440 * Depends on command.
441 *
442 *----------------------------------------------------------------------
443 */
444
445static int
446LogObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
447{
448 const char *strarg;
449 int rc, cmd, result = TCL_OK0;
450 Tcl_DString ds;
451 Log *logPtr = clientData;
452
453 enum {
454 ROLLFMT, MAXBACKUP, MAXBUFFER, EXTHDRS,
455 FLAGS, FILE, ROLL
456 };
457 static const char *const subcmd[] = {
458 "rollfmt", "maxbackup", "maxbuffer", "extendedheaders",
459 "flags", "file", "roll", NULL((void*)0)
460 };
461
462 if (objc < 2) {
463 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
464 return TCL_ERROR1;
465 }
466 rc = Tcl_GetIndexFromObj(interp, objv[1], subcmd, "option", 0, &cmd)Tcl_GetIndexFromObjStruct(interp, objv[1], subcmd, sizeof(char
*), "option", 0, &cmd)
;
467 if (rc != TCL_OK0) {
468 return TCL_ERROR1;
469 }
470
471 switch (cmd) {
472 case ROLLFMT:
473 Ns_MutexLock(&logPtr->lock);
474 if (objc > 2) {
475 strarg = ns_strdup(Tcl_GetString(objv[2]));
476 if (logPtr->rollfmt != NULL((void*)0)) {
477 ns_free((char *)logPtr->rollfmt);
478 }
479 logPtr->rollfmt = strarg;
480 }
481 strarg = logPtr->rollfmt;
482 Ns_MutexUnlock(&logPtr->lock);
483 if (strarg != NULL((void*)0)) {
484 Tcl_SetObjResult(interp, Tcl_NewStringObj(strarg, -1));
485 }
486 break;
487
488 case MAXBACKUP:
489 {
490 int intarg = 0;
491
492 if (objc > 2) {
493 if (Tcl_GetIntFromObj(interp, objv[2], &intarg) != TCL_OK0) {
494 result = TCL_ERROR1;
495 } else {
496 if (intarg < 1) {
497 intarg = 100;
498 }
499 }
500 }
501 if (result == TCL_OK0) {
502 Ns_MutexLock(&logPtr->lock);
503 if (objc > 2) {
504 logPtr->maxbackup = intarg;
505 } else {
506 intarg = logPtr->maxbackup;
507 }
508 Ns_MutexUnlock(&logPtr->lock);
509 Tcl_SetObjResult(interp, Tcl_NewIntObj(intarg));
510 }
511 }
512 break;
513
514 case MAXBUFFER:
515 {
516 int intarg = 0;
517
518 if (objc > 2) {
519 if (Tcl_GetIntFromObj(interp, objv[2], &intarg) != TCL_OK0) {
520 result = TCL_ERROR1;
521 } else {
522 if (intarg < 0) {
523 intarg = 0;
524 }
525 }
526 }
527 if (result == TCL_OK0) {
528 Ns_MutexLock(&logPtr->lock);
529 if (objc > 2) {
530 logPtr->maxlines = intarg;
531 } else {
532 intarg = logPtr->maxlines;
533 }
534 Ns_MutexUnlock(&logPtr->lock);
535 Tcl_SetObjResult(interp, Tcl_NewIntObj(intarg));
536 }
537 }
538 break;
539
540 case EXTHDRS:
541 {
542 Ns_MutexLock(&logPtr->lock);
543 if (objc > 2) {
544 result = ParseExtendedHeaders(logPtr, Tcl_GetString(objv[2]));
545 }
546 if (result == TCL_OK0) {
547 Tcl_SetObjResult(interp, Tcl_NewStringObj(logPtr->extendedHeaders, -1));
548 } else {
549 Ns_TclPrintfResult(interp, "invalid value: %s",
550 Tcl_GetString(objv[2]));
551 }
552 Ns_MutexUnlock(&logPtr->lock);
553 }
554 break;
555
556 case FLAGS:
557 {
558 unsigned int flags;
559
560 Tcl_DStringInit(&ds);
561 if (objc > 2) {
562 flags = 0u;
563 Tcl_DStringAppend(&ds, Tcl_GetString(objv[2]), -1);
564 Ns_StrToLower(ds.string);
565 if (strstr(ds.string, "logcombined")) {
566 flags |= LOG_COMBINED0x01u;
567 }
568 if (strstr(ds.string, "formattedtime")) {
569 flags |= LOG_FMTTIME0x02u;
570 }
571 if (strstr(ds.string, "logreqtime")) {
572 flags |= LOG_REQTIME0x04u;
573 }
574 if (strstr(ds.string, "logpartialtimes")) {
575 flags |= LOG_PARTIALTIMES0x08u;
576 }
577 if (strstr(ds.string, "checkforproxy")) {
578 flags |= LOG_CHECKFORPROXY0x10u;
579 }
580 if (strstr(ds.string, "suppressquery")) {
581 flags |= LOG_SUPPRESSQUERY0x20u;
582 }
583 Tcl_DStringSetLength(&ds, 0);
584 Ns_MutexLock(&logPtr->lock);
585 logPtr->flags = flags;
586 Ns_MutexUnlock(&logPtr->lock);
587 } else {
588 Ns_MutexLock(&logPtr->lock);
589 flags = logPtr->flags;
590 Ns_MutexUnlock(&logPtr->lock);
591 }
592 if ((flags & LOG_COMBINED0x01u)) {
593 Tcl_DStringAppend(&ds, "logcombined ", -1);
594 }
595 if ((flags & LOG_FMTTIME0x02u)) {
596 Tcl_DStringAppend(&ds, "formattedtime ", -1);
597 }
598 if ((flags & LOG_REQTIME0x04u)) {
599 Tcl_DStringAppend(&ds, "logreqtime ", -1);
600 }
601 if ((flags & LOG_PARTIALTIMES0x08u)) {
602 Tcl_DStringAppend(&ds, "logpartialtimes ", -1);
603 }
604 if ((flags & LOG_CHECKFORPROXY0x10u)) {
605 Tcl_DStringAppend(&ds, "checkforproxy ", -1);
606 }
607 if ((flags & LOG_SUPPRESSQUERY0x20u)) {
608 Tcl_DStringAppend(&ds, "suppressquery ", -1);
609 }
610 Tcl_DStringResult(interp, &ds);
611 }
612 break;
613
614 case FILE:
615 if (objc > 2) {
616 Tcl_DStringInit(&ds);
617 strarg = Tcl_GetString(objv[2]);
618 if (Ns_PathIsAbsolute(strarg) == NS_FALSE0) {
619 Ns_HomePath(&ds, strarg, (char *)0L);
620 strarg = ds.string;
621 }
622 Ns_MutexLock(&logPtr->lock);
623 LogClose(logPtr);
624 ns_free((char *)logPtr->filename);
625 logPtr->filename = ns_strdup(strarg);
626 Tcl_DStringFree(&ds);
627 LogOpen(logPtr);
628 } else {
629 Ns_MutexLock(&logPtr->lock);
630 }
631 Tcl_SetObjResult(interp, Tcl_NewStringObj(logPtr->filename, -1));
632 Ns_MutexUnlock(&logPtr->lock);
633 break;
634
635 case ROLL:
636 {
637 Ns_ReturnCode status = NS_ERROR;
638
639 Ns_MutexLock(&logPtr->lock);
640 if (objc == 2) {
641 status = LogRoll(logPtr);
642 } else if (objc > 2) {
643 strarg = Tcl_GetString(objv[2]);
644 if (Tcl_FSAccess(objv[2], F_OK0) == 0) {
645 status = Ns_RollFile(strarg, logPtr->maxbackup);
646 } else {
647 Tcl_Obj *path = Tcl_NewStringObj(logPtr->filename, -1);
648
649 Tcl_IncrRefCount(path)++(path)->refCount;
650 rc = Tcl_FSRenameFile(path, objv[2]);
651 Tcl_DecrRefCount(path)do { Tcl_Obj *_objPtr = (path); if (_objPtr->refCount-- <=
1) { TclFreeObj(_objPtr); } } while(0)
;
652 if (rc != 0) {
653 status = NS_ERROR;
654 } else {
655 LogFlush(logPtr, &logPtr->buffer);
656 status = LogOpen(logPtr);
657 }
658 }
659 }
660 if (status != NS_OK) {
661 Ns_TclPrintfResult(interp, "could not roll \"%s\": %s",
662 logPtr->filename, Tcl_PosixError(interp));
663 }
664 Ns_MutexUnlock(&logPtr->lock);
665 if (status != NS_OK) {
666 result = TCL_ERROR1;
667 }
668 }
669 break;
670 }
671
672 return result;
673}
674
675/*
676 *----------------------------------------------------------------------
677 *
678 * AppendEscaped --
679 *
680 * Append a string with escaped characters
681 *
682 * Results:
683 * None.
684 *
685 * Side effects:
686 * updated dstring
687 *
688 *----------------------------------------------------------------------
689 */
690
691static void
692AppendEscaped(Tcl_DString *dsPtr, const char *toProcess)
693{
694 const char *breakChar;
695
696 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
697 NS_NONNULL_ASSERT(toProcess != NULL)((void) (0));
698
699 do {
700 breakChar = strpbrk(toProcess, "\r\n\t\\\"");
701 if (breakChar == NULL((void*)0)) {
702 /*
703 * No break-char found, append all and stop
704 */
705 Tcl_DStringAppend(dsPtr, toProcess, -1);
706 } else {
707 /*
708 * Append the break-char free prefix
709 */
710 Tcl_DStringAppend(dsPtr, toProcess, (int)(breakChar - toProcess));
711
712 /*
713 * Escape the break-char
714 */
715 switch (*breakChar) {
716 case '\n':
Duplicate code detected
717 Tcl_DStringAppend(dsPtr, "\\n", 2);
718 break;
719 case '\r':
Similar code here
720 Tcl_DStringAppend(dsPtr, "\\r", 2);
721 break;
722 case '\t':
723 Tcl_DStringAppend(dsPtr, "\\t", 2);
724 break;
725 case '"':
726 Tcl_DStringAppend(dsPtr, "\\\"", 2);
727 break;
728 case '\\':
729 Tcl_DStringAppend(dsPtr, "\\\\", 2);
730 break;
731 default:
732 /*should not happen */ assert(0)((void) (0));
733 break;
734 }
735
736 /*
737 * Check for further protected characters after the break char.
738 */
739 toProcess = breakChar + 1;
740 }
741 } while (breakChar != NULL((void*)0));
742}
743
744/*
745 *----------------------------------------------------------------------
746 *
747 * AppendExtHeaders --
748 *
749 * Append named extended header fields from provided set to log entry.
750 *
751 * Results:
752 * None
753 *
754 * Side effects:
755 * Append to Tcl_DString
756 *
757 *----------------------------------------------------------------------
758 */
759
760static void
761AppendExtHeaders(Tcl_DString *dsPtr, const char **argv, const Ns_Set *set)
762{
763 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
764
765 if (set != NULL((void*)0) && argv != NULL((void*)0)) {
766 const char **h;
767
768 for (h = argv; *h != NULL((void*)0); h++) {
769 const char *p;
770
771 Tcl_DStringAppend(dsPtr, " \"", 2);
772 p = Ns_SetIGet(set, *h);
773 if (p != NULL((void*)0)) {
774 AppendEscaped(dsPtr, p);
775 }
776 Tcl_DStringAppend(dsPtr, "\"", 1);
777 }
778 }
779}
780
781
782/*
783 *----------------------------------------------------------------------
784 *
785 * LogTrace --
786 *
787 * Trace routine for appending the log with the current
788 * connection results.
789 *
790 * Results:
791 * None.
792 *
793 * Side effects:
794 * Entry is appended to the open log.
795 *
796 *----------------------------------------------------------------------
797 */
798
799static void
800LogTrace(void *arg, Ns_Conn *conn)
801{
802 Log *logPtr = arg;
803 const char *user, *p, *driverName;
804 char buffer[PIPE_BUF4096], *bufferPtr = NULL((void*)0);
805 int n, i;
806 Ns_ReturnCode status;
807 size_t bufferSize = 0u;
808 Tcl_DString ds, *dsPtr = &ds;
809 char ipString[NS_IPADDR_SIZE46];
810 struct NS_SOCKADDR_STORAGEsockaddr_storage ipStruct, maskedStruct;
811 struct sockaddr *maskPtr = NULL((void*)0),
812 *ipPtr = (struct sockaddr *)&ipStruct,
813 *maskedPtr = (struct sockaddr *)&maskedStruct;
814
815 driverName = Ns_ConnDriverName(conn);
816 Ns_Log(Debug, "nslog called with driver pattern '%s' via driver '%s' req: %s",
817 logPtr->driverPattern, driverName, conn->request.line);
818
819 if (logPtr->driverPattern != NULL((void*)0)
820 && Tcl_StringMatch(driverName, logPtr->driverPattern) == 0
821 ) {
822 /*
823 * This is not for us.
824 */
825 return;
826 }
827
828 Tcl_DStringInit(dsPtr);
829 Ns_MutexLock(&logPtr->lock);
830
831 /*
832 * Append the peer address.
833 */
834 if ((logPtr->flags & LOG_CHECKFORPROXY0x10u) != 0u) {
835 /*
836 * This branch is deprecated and kept only for backward
837 * compatibility (added Dec 2020).
838 */
839 p = Ns_ConnForwardedPeerAddr(conn);
840 if (*p == '\0') {
841 p = Ns_ConnPeerAddr(conn);
842 }
843 } else {
844 p = Ns_ConnConfiguredPeerAddr(conn);
845 }
846
847 /*
848 * Check if the actual IP address can be converted to internal format (this
849 * should be always possible).
850 */
851 if ((logPtr->flags |= LOG_MASKIP0x80u)
852 && (ns_inet_pton(ipPtr, p) == 1)
853 ) {
854
855 /*
856 * Depending on the class of the IP address, use the appropriate mask.
857 */
858 if (ipPtr->sa_family == AF_INET2) {
859 maskPtr = logPtr->ipv4maskPtr;
860 }
861#ifdef HAVE_IPV61
862 if (ipPtr->sa_family == AF_INET610) {
863 maskPtr = logPtr->ipv6maskPtr;
864 }
865#endif
866 /*
867 * If the mask is non-null, the IP "anonymizing" was configured.
868 */
869 if (maskPtr != NULL((void*)0)) {
870 Ns_SockaddrMask(ipPtr, maskPtr, maskedPtr);
871 ns_inet_ntop(maskedPtr, ipString, NS_IPADDR_SIZE46);
872 p = ipString;
873 }
874 }
875
876 Tcl_DStringAppend(dsPtr, p, -1);
877
878 /*
879 * Append the thread name, if requested.
880 * This eases to link access-log with error-log entries
881 */
882 Tcl_DStringAppend(dsPtr, " ", 1);
883 if ((logPtr->flags & LOG_THREADNAME0x40u) != 0) {
884 Tcl_DStringAppend(dsPtr, Ns_ThreadGetName(), -1);
885 Tcl_DStringAppend(dsPtr, " ", 1);
886 } else {
887 Tcl_DStringAppend(dsPtr, "- ", 2);
888 }
889
890 /*
891 * Append the authorized user, if any. Watch usernames
892 * with embedded blanks; we must properly quote them.
893 */
894
895 user = Ns_ConnAuthUser(conn);
896 if (user == NULL((void*)0)) {
897 Tcl_DStringAppend(dsPtr, "- ", 2);
898 } else {
899 int quote = 0;
900
901 for (p = user; *p && !quote; p++) {
902 quote = (CHARTYPE(space, *p)(((*__ctype_b_loc ())[(int) (((int)((unsigned char)(*p))))] &
(unsigned short int) _ISspace))
!= 0);
903 }
904 if (quote != 0) {
905 Tcl_DStringAppend(dsPtr, "\"", 1);
906 Tcl_DStringAppend(dsPtr, user, -1);
907 Tcl_DStringAppend(dsPtr, "\" ", 2);
908 } else {
909 Tcl_DStringAppend(dsPtr, user, -1);
910 Tcl_DStringAppend(dsPtr, " ", 1);
911 }
912 }
913
914 /*
915 * Append a common log format timestamp including GMT offset
916 */
917
918 if (!(logPtr->flags & LOG_FMTTIME0x02u)) {
919 Ns_DStringPrintf(dsPtr, "[%" PRId64"l" "d" "]", (int64_t) time(NULL((void*)0)));
920 } else {
921 char buf[41]; /* Big enough for Ns_LogTime(). */
922
923 Ns_LogTime(buf);
924 Tcl_DStringAppend(dsPtr, buf, -1);
925 }
926
927 /*
928 * Append the request line plus query data (if configured)
929 */
930
931 if (likely(conn->request.line != NULL)(__builtin_expect((conn->request.line != ((void*)0)), 1))) {
932 const char *string = (logPtr->flags & LOG_SUPPRESSQUERY0x20u) ?
933 conn->request.url :
934 conn->request.line;
935
936 Tcl_DStringAppend(dsPtr, " \"", 2);
937 if (likely(string != NULL)(__builtin_expect((string != ((void*)0)), 1))) {
938 AppendEscaped(dsPtr, string);
939 }
940 Tcl_DStringAppend(dsPtr, "\" ", 2);
941
942 } else {
943 Tcl_DStringAppend(dsPtr, " \"\" ", 4);
944 }
945
946 /*
947 * Construct and append the HTTP status code and bytes sent
948 */
949
950 n = Ns_ConnResponseStatus(conn);
951 Ns_DStringPrintf(dsPtr, "%d %" PRIdz"zd", (n != 0) ? n : 200, Ns_ConnContentSent(conn));
952
953 /*
954 * Append the referrer (using the misspelled header field "Referer") and
955 * user-agent headers (if any)
956 */
957
958 if ((logPtr->flags & LOG_COMBINED0x01u)) {
959
960 Tcl_DStringAppend(dsPtr, " \"", 2);
961 p = Ns_SetIGet(conn->headers, "referer");
962 if (p != NULL((void*)0)) {
963 AppendEscaped(dsPtr, p);
964 }
965 Tcl_DStringAppend(dsPtr, "\" \"", 3);
966 p = Ns_SetIGet(conn->headers, "user-agent");
967 if (p != NULL((void*)0)) {
968 AppendEscaped(dsPtr, p);
969 }
970 Tcl_DStringAppend(dsPtr, "\"", 1);
971 }
972
973 /*
974 * Append the request's elapsed time and queue time (if enabled)
975 */
976
977 if ((logPtr->flags & LOG_REQTIME0x04u) != 0u) {
978 Ns_Time reqTime, now;
979 Ns_GetTime(&now);
980 Ns_DiffTime(&now, Ns_ConnStartTime(conn), &reqTime);
981 Tcl_DStringAppend(dsPtr, " ", 1);
982 Ns_DStringAppendTime(dsPtr, &reqTime);
983
984 }
985
986 if ((logPtr->flags & LOG_PARTIALTIMES0x08u) != 0u) {
987 Ns_Time acceptTime, queueTime, filterTime, runTime;
988 Ns_Time *startTimePtr = Ns_ConnStartTime(conn);
989
990 Ns_ConnTimeSpans(conn, &acceptTime, &queueTime, &filterTime, &runTime);
991
992 Tcl_DStringAppend(dsPtr, " \"", 2);
993 Ns_DStringAppendTime(dsPtr, startTimePtr);
994 Tcl_DStringAppend(dsPtr, " ", 1);
995 Ns_DStringAppendTime(dsPtr, &acceptTime);
996 Tcl_DStringAppend(dsPtr, " ", 1);
997 Ns_DStringAppendTime(dsPtr, &queueTime);
998 Tcl_DStringAppend(dsPtr, " ", 1);
999 Ns_DStringAppendTime(dsPtr, &filterTime);
1000 Tcl_DStringAppend(dsPtr, " ", 1);
1001 Ns_DStringAppendTime(dsPtr, &runTime);
1002 Tcl_DStringAppend(dsPtr, "\"", 1);
1003 }
1004
1005 /*
1006 * Append the extended headers (if any)
1007 */
1008 AppendExtHeaders(dsPtr, logPtr->requestHeaders, conn->headers);
1009 AppendExtHeaders(dsPtr, logPtr->responseHeaders, conn->outputheaders);
1010
1011 for (i = 0; i < dsPtr->length; i++) {
1012 /*
1013 * Quick fix to disallow terminal escape characters in the log
1014 * file. See e.g. http://www.securityfocus.com/bid/37712/info
1015 */
1016 if (unlikely(dsPtr->string[i] == 0x1b)(__builtin_expect((dsPtr->string[i] == 0x1b), 0))) {
1017 dsPtr->string[i] = 7; /* bell */
1018 }
1019 }
1020
1021 Ns_Log(Ns_LogAccessDebug, "%s", dsPtr->string);
1022
1023 /*
1024 * Append the trailing newline and optionally
1025 * flush the buffer
1026 */
1027
1028 Tcl_DStringAppend(dsPtr, "\n", 1);
1029
1030 if (logPtr->maxlines == 0) {
1031 bufferSize = (size_t)dsPtr->length;
1032 if (bufferSize < PIPE_BUF4096) {
1033 /*
1034 * Only ns_write() operations < PIPE_BUF are guaranteed to be atomic
1035 */
1036 bufferPtr = dsPtr->string;
1037 status = NS_OK;
1038 } else {
1039 status = LogFlush(logPtr, dsPtr);
1040 }
1041 } else {
1042 Tcl_DStringAppend(&logPtr->buffer, dsPtr->string, dsPtr->length);
1043 if (++logPtr->curlines > logPtr->maxlines) {
1044 bufferSize = (size_t)logPtr->buffer.length;
1045 if (bufferSize < PIPE_BUF4096) {
1046 /*
1047 * Only ns_write() operations < PIPE_BUF are guaranteed to be
1048 * atomic. In most cases, the other branch is used.
1049 */
1050 memcpy(buffer, logPtr->buffer.string, bufferSize);
1051 bufferPtr = buffer;
1052 Tcl_DStringSetLength(&logPtr->buffer, 0);
1053 status = NS_OK;
1054 } else {
1055 status = LogFlush(logPtr, &logPtr->buffer);
1056 }
1057 logPtr->curlines = 0;
1058 } else {
1059 status = NS_OK;
1060 }
1061 }
1062 Ns_MutexUnlock(&logPtr->lock);
1063 (void)(status); /* ignore status */
1064
1065 if (likely(bufferPtr != NULL)(__builtin_expect((bufferPtr != ((void*)0)), 1)) && likely(logPtr->fd >= 0)(__builtin_expect((logPtr->fd >= 0), 1)) && likely(bufferSize > 0)(__builtin_expect((bufferSize > 0), 1))) {
1066 (void)NsAsyncWrite(logPtr->fd, bufferPtr, bufferSize);
1067 }
1068
1069 Tcl_DStringFree(dsPtr);
1070}
1071
1072
1073/*
1074 *----------------------------------------------------------------------
1075 *
1076 * LogOpen --
1077 *
1078 * Open the access log, closing previous log if opened.
1079 * Assume caller is holding the log mutex.
1080 *
1081 * Results:
1082 * NS_OK or NS_ERROR.
1083 *
1084 * Side effects:
1085 * Log re-opened.
1086 *
1087 *----------------------------------------------------------------------
1088 */
1089
1090static Ns_ReturnCode
1091LogOpen(void *arg)
1092{
1093 int fd;
1094 Ns_ReturnCode status;
1095 Log *logPtr = (Log *)arg;
1096
1097 fd = ns_openopen(logPtr->filename, O_APPEND02000 | O_WRONLY01 | O_CREAT0100 | O_CLOEXEC02000000, 0644);
1098 if (fd == NS_INVALID_FD(-1)) {
1099 Ns_Log(Error, "nslog: error '%s' opening '%s'",
1100 strerror(errno(*__errno_location ())), logPtr->filename);
1101 status = NS_ERROR;
1102 } else {
1103 status = NS_OK;
1104 if (logPtr->fd >= 0) {
1105 ns_closeclose(logPtr->fd);
1106 }
1107
1108 logPtr->fd = fd;
1109 Ns_Log(Notice, "nslog: opened '%s'", logPtr->filename);
1110 }
1111
1112 return status;
1113}
1114
1115
1116/*
1117 *----------------------------------------------------------------------
1118 *
1119 * LogClose --
1120 *
1121 * Flush and/or close the log.
1122 * Assume caller is holding the log mutex.
1123 *
1124 * Results:
1125 * NS_TRUE or NS_FALSE if log was closed.
1126 *
1127 * Side effects:
1128 * Buffer entries, if any, are flushed.
1129 *
1130 *----------------------------------------------------------------------
1131 */
1132
1133static Ns_ReturnCode
1134LogClose(void *arg)
1135{
1136 Ns_ReturnCode status = NS_OK;
1137 Log *logPtr = (Log *)arg;
1138
1139 if (logPtr->fd >= 0) {
1140 status = LogFlush(logPtr, &logPtr->buffer);
1141 ns_closeclose(logPtr->fd);
1142 logPtr->fd = NS_INVALID_FD(-1);
1143 Tcl_DStringFree(&logPtr->buffer);
1144 Ns_Log(Notice, "nslog: closed '%s'", logPtr->filename);
1145 }
1146
1147 return status;
1148}
1149
1150
1151/*
1152 *----------------------------------------------------------------------
1153 *
1154 * LogFlush --
1155 *
1156 * Flush a log buffer to the open log file.
1157 * Assume caller is holding the log mutex.
1158 *
1159 * Results:
1160 * None.
1161 *
1162 * Side effects:
1163 * Will disable the log on error.
1164 *
1165 *----------------------------------------------------------------------
1166 */
1167
1168static Ns_ReturnCode
1169LogFlush(Log *logPtr, Tcl_DString *dsPtr)
1170{
1171 int len = dsPtr->length;
1172 char *buf = dsPtr->string;
1173
1174 if (len > 0) {
1175 if (logPtr->fd >= 0 && ns_writewrite(logPtr->fd, buf, (size_t)len) != len) {
1176 Ns_Log(Error, "nslog: logging disabled: ns_write() failed: '%s'",
1177 strerror(errno(*__errno_location ())));
1178 ns_closeclose(logPtr->fd);
1179 logPtr->fd = NS_INVALID_FD(-1);
1180 }
1181 Tcl_DStringSetLength(dsPtr, 0);
1182 }
1183
1184 return (logPtr->fd == NS_INVALID_FD(-1)) ? NS_ERROR : NS_OK;
1185}
1186
1187
1188/*
1189 *----------------------------------------------------------------------
1190 *
1191 * LogRoll --
1192 *
1193 * Roll and re-open the access log. This procedure is scheduled
1194 * and/or registered at signal catching.
1195 *
1196 * Assume caller is holding the log mutex.
1197 *
1198 * Results:
1199 * None.
1200 *
1201 * Side effects:
1202 * Files are rolled to new names.
1203 *
1204 *----------------------------------------------------------------------
1205 */
1206
1207static Ns_ReturnCode
1208LogRoll(void *arg)
1209{
1210 Ns_ReturnCode status;
1211 Log *logPtr = (Log *)arg;
1212
1213 status = Ns_RollFileCondFmt(LogOpen, LogClose, logPtr,
1214 logPtr->filename,
1215 logPtr->rollfmt,
1216 logPtr->maxbackup);
1217
1218 //if (status == NS_OK) {
1219 // status = LogOpen(logPtr);
1220 //}
1221
1222 return status;
1223}
1224
1225
1226/*
1227 *----------------------------------------------------------------------
1228 *
1229 * LogCloseCallback, LogRollCallback -
1230 *
1231 * Close or roll the log.
1232 *
1233 * Results:
1234 * None.
1235 *
1236 * Side effects:
1237 * See LogClose and LogRoll.
1238 *
1239 *----------------------------------------------------------------------
1240 */
1241
1242static void
1243LogCallbackProc(Ns_LogCallbackProc proc, void *arg, const char *desc)
1244{
1245 int status;
1246 Log *logPtr = arg;
1247
1248 Ns_MutexLock(&logPtr->lock);
1249 status =(*proc)(logPtr);
1250 Ns_MutexUnlock(&logPtr->lock);
1251
1252 if (status != NS_OK) {
1253 Ns_Log(Error, "nslog: failed: %s '%s': '%s'", desc, logPtr->filename,
1254 strerror(Tcl_GetErrno()));
1255 }
1256}
1257
1258static void
1259LogCloseCallback(const Ns_Time *toPtr, void *arg)
1260{
1261 if (toPtr == NULL((void*)0)) {
1262 LogCallbackProc(LogClose, arg, "close");
1263 }
1264}
1265
1266static void
1267LogRollCallback(void *arg, int UNUSED(id)UNUSED_id __attribute__((__unused__)))
1268{
1269 LogCallbackProc(LogRoll, arg, "roll");
1270}
1271
1272
1273/*
1274 *----------------------------------------------------------------------
1275 *
1276 * LogArg --
1277 *
1278 * Copy log filename as argument for callback introspection queries.
1279 *
1280 * Results:
1281 * None.
1282 *
1283 * Side effects:
1284 * None.
1285 *
1286 *----------------------------------------------------------------------
1287 */
1288
1289static void
1290LogArg(Tcl_DString *dsPtr, const void *arg)
1291{
1292 const Log *logPtr = arg;
1293
1294 Tcl_DStringAppendElement(dsPtr, logPtr->filename);
1295}
1296
1297/*
1298 * Local Variables:
1299 * mode: c
1300 * c-basic-offset: 4
1301 * fill-column: 78
1302 * indent-tabs-mode: nil
1303 * End:
1304 */