Coverage Report

Created: 2023-11-19 06:57

/src/nginx/src/core/ngx_syslog.c
Line
Count
Source (jump to first uncovered line)
1
2
/*
3
 * Copyright (C) Nginx, Inc.
4
 */
5
6
7
#include <ngx_config.h>
8
#include <ngx_core.h>
9
#include <ngx_event.h>
10
11
12
#define NGX_SYSLOG_MAX_STR                                                    \
13
0
    NGX_MAX_ERROR_STR + sizeof("<255>Jan 01 00:00:00 ") - 1                   \
14
0
    + (NGX_MAXHOSTNAMELEN - 1) + 1 /* space */                                \
15
0
    + 32 /* tag */ + 2 /* colon, space */
16
17
18
static char *ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer);
19
static ngx_int_t ngx_syslog_init_peer(ngx_syslog_peer_t *peer);
20
static void ngx_syslog_cleanup(void *data);
21
static u_char *ngx_syslog_log_error(ngx_log_t *log, u_char *buf, size_t len);
22
23
24
static char  *facilities[] = {
25
    "kern", "user", "mail", "daemon", "auth", "intern", "lpr", "news", "uucp",
26
    "clock", "authpriv", "ftp", "ntp", "audit", "alert", "cron", "local0",
27
    "local1", "local2", "local3", "local4", "local5", "local6", "local7",
28
    NULL
29
};
30
31
/* note 'error/warn' like in nginx.conf, not 'err/warning' */
32
static char  *severities[] = {
33
    "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", NULL
34
};
35
36
static ngx_log_t    ngx_syslog_dummy_log;
37
static ngx_event_t  ngx_syslog_dummy_event;
38
39
40
char *
41
ngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer)
42
0
{
43
0
    ngx_pool_cleanup_t  *cln;
44
45
0
    peer->facility = NGX_CONF_UNSET_UINT;
46
0
    peer->severity = NGX_CONF_UNSET_UINT;
47
48
0
    if (ngx_syslog_parse_args(cf, peer) != NGX_CONF_OK) {
49
0
        return NGX_CONF_ERROR;
50
0
    }
51
52
0
    if (peer->server.sockaddr == NULL) {
53
0
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
54
0
                           "no syslog server specified");
55
0
        return NGX_CONF_ERROR;
56
0
    }
57
58
0
    if (peer->facility == NGX_CONF_UNSET_UINT) {
59
0
        peer->facility = 23; /* local7 */
60
0
    }
61
62
0
    if (peer->severity == NGX_CONF_UNSET_UINT) {
63
0
        peer->severity = 6; /* info */
64
0
    }
65
66
0
    if (peer->tag.data == NULL) {
67
0
        ngx_str_set(&peer->tag, "nginx");
68
0
    }
69
70
0
    peer->hostname = &cf->cycle->hostname;
71
0
    peer->logp = &cf->cycle->new_log;
72
73
0
    peer->conn.fd = (ngx_socket_t) -1;
74
75
0
    peer->conn.read = &ngx_syslog_dummy_event;
76
0
    peer->conn.write = &ngx_syslog_dummy_event;
77
78
0
    ngx_syslog_dummy_event.log = &ngx_syslog_dummy_log;
79
80
0
    cln = ngx_pool_cleanup_add(cf->pool, 0);
81
0
    if (cln == NULL) {
82
0
        return NGX_CONF_ERROR;
83
0
    }
84
85
0
    cln->data = peer;
86
0
    cln->handler = ngx_syslog_cleanup;
87
88
0
    return NGX_CONF_OK;
89
0
}
90
91
92
static char *
93
ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer)
94
0
{
95
0
    u_char      *p, *comma, c;
96
0
    size_t       len;
97
0
    ngx_str_t   *value;
98
0
    ngx_url_t    u;
99
0
    ngx_uint_t   i;
100
101
0
    value = cf->args->elts;
102
103
0
    p = value[1].data + sizeof("syslog:") - 1;
104
105
0
    for ( ;; ) {
106
0
        comma = (u_char *) ngx_strchr(p, ',');
107
108
0
        if (comma != NULL) {
109
0
            len = comma - p;
110
0
            *comma = '\0';
111
112
0
        } else {
113
0
            len = value[1].data + value[1].len - p;
114
0
        }
115
116
0
        if (ngx_strncmp(p, "server=", 7) == 0) {
117
118
0
            if (peer->server.sockaddr != NULL) {
119
0
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
120
0
                                   "duplicate syslog \"server\"");
121
0
                return NGX_CONF_ERROR;
122
0
            }
123
124
0
            ngx_memzero(&u, sizeof(ngx_url_t));
125
126
0
            u.url.data = p + 7;
127
0
            u.url.len = len - 7;
128
0
            u.default_port = 514;
129
130
0
            if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
131
0
                if (u.err) {
132
0
                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
133
0
                                       "%s in syslog server \"%V\"",
134
0
                                       u.err, &u.url);
135
0
                }
136
137
0
                return NGX_CONF_ERROR;
138
0
            }
139
140
0
            peer->server = u.addrs[0];
141
142
0
        } else if (ngx_strncmp(p, "facility=", 9) == 0) {
143
144
0
            if (peer->facility != NGX_CONF_UNSET_UINT) {
145
0
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
146
0
                                   "duplicate syslog \"facility\"");
147
0
                return NGX_CONF_ERROR;
148
0
            }
149
150
0
            for (i = 0; facilities[i] != NULL; i++) {
151
152
0
                if (ngx_strcmp(p + 9, facilities[i]) == 0) {
153
0
                    peer->facility = i;
154
0
                    goto next;
155
0
                }
156
0
            }
157
158
0
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
159
0
                               "unknown syslog facility \"%s\"", p + 9);
160
0
            return NGX_CONF_ERROR;
161
162
0
        } else if (ngx_strncmp(p, "severity=", 9) == 0) {
163
164
0
            if (peer->severity != NGX_CONF_UNSET_UINT) {
165
0
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
166
0
                                   "duplicate syslog \"severity\"");
167
0
                return NGX_CONF_ERROR;
168
0
            }
169
170
0
            for (i = 0; severities[i] != NULL; i++) {
171
172
0
                if (ngx_strcmp(p + 9, severities[i]) == 0) {
173
0
                    peer->severity = i;
174
0
                    goto next;
175
0
                }
176
0
            }
177
178
0
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
179
0
                               "unknown syslog severity \"%s\"", p + 9);
180
0
            return NGX_CONF_ERROR;
181
182
0
        } else if (ngx_strncmp(p, "tag=", 4) == 0) {
183
184
0
            if (peer->tag.data != NULL) {
185
0
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
186
0
                                   "duplicate syslog \"tag\"");
187
0
                return NGX_CONF_ERROR;
188
0
            }
189
190
            /*
191
             * RFC 3164: the TAG is a string of ABNF alphanumeric characters
192
             * that MUST NOT exceed 32 characters.
193
             */
194
0
            if (len - 4 > 32) {
195
0
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
196
0
                                   "syslog tag length exceeds 32");
197
0
                return NGX_CONF_ERROR;
198
0
            }
199
200
0
            for (i = 4; i < len; i++) {
201
0
                c = ngx_tolower(p[i]);
202
203
0
                if (c < '0' || (c > '9' && c < 'a' && c != '_') || c > 'z') {
204
0
                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
205
0
                                       "syslog \"tag\" only allows "
206
0
                                       "alphanumeric characters "
207
0
                                       "and underscore");
208
0
                    return NGX_CONF_ERROR;
209
0
                }
210
0
            }
211
212
0
            peer->tag.data = p + 4;
213
0
            peer->tag.len = len - 4;
214
215
0
        } else if (len == 10 && ngx_strncmp(p, "nohostname", 10) == 0) {
216
0
            peer->nohostname = 1;
217
218
0
        } else {
219
0
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
220
0
                               "unknown syslog parameter \"%s\"", p);
221
0
            return NGX_CONF_ERROR;
222
0
        }
223
224
0
    next:
225
226
0
        if (comma == NULL) {
227
0
            break;
228
0
        }
229
230
0
        p = comma + 1;
231
0
    }
232
233
0
    return NGX_CONF_OK;
234
0
}
235
236
237
u_char *
238
ngx_syslog_add_header(ngx_syslog_peer_t *peer, u_char *buf)
239
0
{
240
0
    ngx_uint_t  pri;
241
242
0
    pri = peer->facility * 8 + peer->severity;
243
244
0
    if (peer->nohostname) {
245
0
        return ngx_sprintf(buf, "<%ui>%V %V: ", pri, &ngx_cached_syslog_time,
246
0
                           &peer->tag);
247
0
    }
248
249
0
    return ngx_sprintf(buf, "<%ui>%V %V %V: ", pri, &ngx_cached_syslog_time,
250
0
                       peer->hostname, &peer->tag);
251
0
}
252
253
254
void
255
ngx_syslog_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf,
256
    size_t len)
257
0
{
258
0
    u_char             *p, msg[NGX_SYSLOG_MAX_STR];
259
0
    ngx_uint_t          head_len;
260
0
    ngx_syslog_peer_t  *peer;
261
262
0
    peer = log->wdata;
263
264
0
    if (peer->busy) {
265
0
        return;
266
0
    }
267
268
0
    peer->busy = 1;
269
0
    peer->severity = level - 1;
270
271
0
    p = ngx_syslog_add_header(peer, msg);
272
0
    head_len = p - msg;
273
274
0
    len -= NGX_LINEFEED_SIZE;
275
276
0
    if (len > NGX_SYSLOG_MAX_STR - head_len) {
277
0
        len = NGX_SYSLOG_MAX_STR - head_len;
278
0
    }
279
280
0
    p = ngx_snprintf(p, len, "%s", buf);
281
282
0
    (void) ngx_syslog_send(peer, msg, p - msg);
283
284
0
    peer->busy = 0;
285
0
}
286
287
288
ssize_t
289
ngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len)
290
0
{
291
0
    ssize_t  n;
292
293
0
    if (peer->log.handler == NULL) {
294
0
        peer->log = *peer->logp;
295
0
        peer->log.handler = ngx_syslog_log_error;
296
0
        peer->log.data = peer;
297
0
        peer->log.action = "logging to syslog";
298
0
    }
299
300
0
    if (peer->conn.fd == (ngx_socket_t) -1) {
301
0
        if (ngx_syslog_init_peer(peer) != NGX_OK) {
302
0
            return NGX_ERROR;
303
0
        }
304
0
    }
305
306
0
    if (ngx_send) {
307
0
        n = ngx_send(&peer->conn, buf, len);
308
309
0
    } else {
310
        /* event module has not yet set ngx_io */
311
0
        n = ngx_os_io.send(&peer->conn, buf, len);
312
0
    }
313
314
0
    if (n == NGX_ERROR) {
315
316
0
        if (ngx_close_socket(peer->conn.fd) == -1) {
317
0
            ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,
318
0
                          ngx_close_socket_n " failed");
319
0
        }
320
321
0
        peer->conn.fd = (ngx_socket_t) -1;
322
0
    }
323
324
0
    return n;
325
0
}
326
327
328
static ngx_int_t
329
ngx_syslog_init_peer(ngx_syslog_peer_t *peer)
330
0
{
331
0
    ngx_socket_t  fd;
332
333
0
    fd = ngx_socket(peer->server.sockaddr->sa_family, SOCK_DGRAM, 0);
334
0
    if (fd == (ngx_socket_t) -1) {
335
0
        ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,
336
0
                      ngx_socket_n " failed");
337
0
        return NGX_ERROR;
338
0
    }
339
340
0
    if (ngx_nonblocking(fd) == -1) {
341
0
        ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,
342
0
                      ngx_nonblocking_n " failed");
343
0
        goto failed;
344
0
    }
345
346
0
    if (connect(fd, peer->server.sockaddr, peer->server.socklen) == -1) {
347
0
        ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,
348
0
                      "connect() failed");
349
0
        goto failed;
350
0
    }
351
352
0
    peer->conn.fd = fd;
353
0
    peer->conn.log = &peer->log;
354
355
    /* UDP sockets are always ready to write */
356
0
    peer->conn.write->ready = 1;
357
358
0
    return NGX_OK;
359
360
0
failed:
361
362
0
    if (ngx_close_socket(fd) == -1) {
363
0
        ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,
364
0
                      ngx_close_socket_n " failed");
365
0
    }
366
367
0
    return NGX_ERROR;
368
0
}
369
370
371
static void
372
ngx_syslog_cleanup(void *data)
373
0
{
374
0
    ngx_syslog_peer_t  *peer = data;
375
376
    /* prevents further use of this peer */
377
0
    peer->busy = 1;
378
379
0
    if (peer->conn.fd == (ngx_socket_t) -1) {
380
0
        return;
381
0
    }
382
383
0
    if (ngx_close_socket(peer->conn.fd) == -1) {
384
0
        ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,
385
0
                      ngx_close_socket_n " failed");
386
0
    }
387
0
}
388
389
390
static u_char *
391
ngx_syslog_log_error(ngx_log_t *log, u_char *buf, size_t len)
392
0
{
393
0
    u_char             *p;
394
0
    ngx_syslog_peer_t  *peer;
395
396
0
    p = buf;
397
398
0
    if (log->action) {
399
0
        p = ngx_snprintf(buf, len, " while %s", log->action);
400
0
        len -= p - buf;
401
0
    }
402
403
0
    peer = log->data;
404
405
0
    if (peer) {
406
0
        p = ngx_snprintf(p, len, ", server: %V", &peer->server.name);
407
0
    }
408
409
0
    return p;
410
0
}