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