1 #include "LogSyslogFast.h"
2 
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <netdb.h>
6 #include <netinet/in.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/socket.h>
11 #include <sys/types.h>
12 #include <sys/un.h>
13 #include <unistd.h>
14 
15 #define INITIAL_BUFSIZE 2048
16 
17 static
18 void
update_prefix(LogSyslogFast * logger,time_t t)19 update_prefix(LogSyslogFast* logger, time_t t)
20 {
21     if (!logger->sender || !logger->name)
22         return; /* still initializing */
23 
24     logger->last_time = t;
25 
26     /* LOG_RFC3164 time string tops out at 15 chars, LOG_RFC5424 at 25 */
27     char timestr[26];
28     strftime(timestr, 26, logger->time_format, localtime(&t));
29 
30     /* %z in strftime returns 4DIGIT, but we need 2DIGIT ":" 2DIGIT */
31     if (logger->format == LOG_RFC5424) {
32         timestr[25] = 0;
33         timestr[24] = timestr[23];
34         timestr[23] = timestr[22];
35         timestr[22] = ':';
36     }
37 
38     if (logger->format == LOG_RFC3164 || logger->format == LOG_RFC5424) {
39         logger->prefix_len = snprintf(
40             logger->linebuf, logger->bufsize, logger->msg_format,
41             logger->priority, timestr, logger->sender, logger->name, logger->pid
42         );
43     } else if (logger->format == LOG_RFC3164_LOCAL) { /* without sender */
44         logger->prefix_len = snprintf(
45             logger->linebuf, logger->bufsize, logger->msg_format,
46             logger->priority, timestr, logger->name, logger->pid
47         );
48     }
49 
50     if (logger->prefix_len > logger->bufsize - 1)
51         logger->prefix_len = logger->bufsize - 1;
52 
53     /* cache the location in linebuf where msg should be pasted in */
54     logger->msg_start = logger->linebuf + logger->prefix_len;
55 }
56 
57 LogSyslogFast*
LSF_alloc()58 LSF_alloc()
59 {
60     return malloc(sizeof(LogSyslogFast));
61 }
62 
63 int
LSF_init(LogSyslogFast * logger,int proto,const char * hostname,int port,int facility,int severity,const char * sender,const char * name)64 LSF_init(
65     LogSyslogFast* logger, int proto, const char* hostname, int port,
66     int facility, int severity, const char* sender, const char* name)
67 {
68     if (!logger)
69         return -1;
70 
71     logger->sock = -1;
72 
73     logger->pid = getpid();
74 
75     logger->linebuf = malloc(logger->bufsize = INITIAL_BUFSIZE);
76     if (!logger->linebuf) {
77         logger->err = strerror(errno);
78         return -1;
79     }
80 
81     logger->sender = NULL;
82     logger->name = NULL;
83     LSF_set_format(logger, LOG_RFC3164);
84     LSF_set_sender(logger, sender);
85     LSF_set_name(logger, name);
86 
87     logger->priority = (facility << 3) | severity;
88     update_prefix(logger, time(0));
89 
90     return LSF_set_receiver(logger, proto, hostname, port);
91 }
92 
93 int
LSF_destroy(LogSyslogFast * logger)94 LSF_destroy(LogSyslogFast* logger)
95 {
96     int ret = close(logger->sock);
97     if (ret)
98         logger->err = strerror(errno);
99     free(logger->sender);
100     free(logger->name);
101     free(logger->linebuf);
102     free(logger);
103     return ret;
104 }
105 
106 void
LSF_set_priority(LogSyslogFast * logger,int facility,int severity)107 LSF_set_priority(LogSyslogFast* logger, int facility, int severity)
108 {
109     logger->priority = (facility << 3) | severity;
110     update_prefix(logger, time(0));
111 }
112 
113 void
LSF_set_facility(LogSyslogFast * logger,int facility)114 LSF_set_facility(LogSyslogFast* logger, int facility)
115 {
116     LSF_set_priority(logger, facility, LSF_get_severity(logger));
117 }
118 
119 void
LSF_set_severity(LogSyslogFast * logger,int severity)120 LSF_set_severity(LogSyslogFast* logger, int severity)
121 {
122     LSF_set_priority(logger, LSF_get_facility(logger), severity);
123 }
124 
125 int
LSF_set_sender(LogSyslogFast * logger,const char * sender)126 LSF_set_sender(LogSyslogFast* logger, const char* sender)
127 {
128     free(logger->sender);
129     logger->sender = strdup(sender);
130     if (!logger->sender) {
131         logger->err = "strdup failure in set_sender";
132         return -1;
133     }
134     update_prefix(logger, time(0));
135     return 0;
136 }
137 
138 int
LSF_set_name(LogSyslogFast * logger,const char * name)139 LSF_set_name(LogSyslogFast* logger, const char* name)
140 {
141     free(logger->name);
142     logger->name = strdup(name);
143     if (!logger->name) {
144         logger->err = "strdup failure in set_name";
145         return -1;
146     }
147     update_prefix(logger, time(0));
148     return 0;
149 }
150 
151 void
LSF_set_pid(LogSyslogFast * logger,int pid)152 LSF_set_pid(LogSyslogFast* logger, int pid)
153 {
154     logger->pid = pid;
155     update_prefix(logger, time(0));
156 }
157 
158 int
LSF_set_format(LogSyslogFast * logger,int format)159 LSF_set_format(LogSyslogFast* logger, int format)
160 {
161     logger->format = format;
162 
163     /* msg_format must contain format strings for:
164        1) priority (int)
165        2) timestamp (string)
166        3) hostname (string)
167        4) app name (string)
168        5) pid (int)
169     */
170     if (logger->format == LOG_RFC3164) {
171         /*
172            'The TIMESTAMP field is the local time and is in the format of
173            "Mmm dd hh:mm:ss"'
174 
175             Example: "Jan  4 11:22:33"
176         */
177         logger->time_format = "%h %e %H:%M:%S";
178         logger->msg_format = "<%d>%s %s %s[%d]: ";
179     }
180     else if (logger->format == LOG_RFC5424) {
181         /*
182             TIMESTAMP       = NILVALUE / FULL-DATE "T" FULL-TIME
183             FULL-DATE       = DATE-FULLYEAR "-" DATE-MONTH "-" DATE-MDAY
184             DATE-FULLYEAR   = 4DIGIT
185             DATE-MONTH      = 2DIGIT  ; 01-12
186             DATE-MDAY       = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on
187                                         ; month/year
188             FULL-TIME       = PARTIAL-TIME TIME-OFFSET
189             PARTIAL-TIME    = TIME-HOUR ":" TIME-MINUTE ":" TIME-SECOND
190                                 [TIME-SECFRAC]
191             TIME-HOUR       = 2DIGIT  ; 00-23
192             TIME-MINUTE     = 2DIGIT  ; 00-59
193             TIME-SECOND     = 2DIGIT  ; 00-59
194             TIME-SECFRAC    = "." 1*6DIGIT
195             TIME-OFFSET     = "Z" / TIME-NUMOFFSET
196             TIME-NUMOFFSET  = ("+" / "-") TIME-HOUR ":" TIME-MINUTE
197 
198             Example: "2012-01-04T11:22:33-0800"
199         */
200         logger->time_format = "%Y-%m-%dT%H:%M:%S%z";
201 
202         /* STRUCTURED-DATA and MSGID fields are omitted */
203         logger->msg_format = "<%d>1 %s %s %s %d - - ";
204     }
205     else if (logger->format == LOG_RFC3164_LOCAL) {
206         /* Same as LOG_RFC3164 but without HOSTNAME */
207         logger->time_format = "%h %e %H:%M:%S";
208         logger->msg_format = "<%d>%s %s[%d]: ";
209     }
210     else {
211         logger->err = "invalid format constant";
212         return -1;
213     }
214     update_prefix(logger, time(0));
215     return 0;
216 }
217 
218 #ifdef AF_INET6
219 #define clean_return(x) if (results) freeaddrinfo(results); return x;
220 #else
221 #define clean_return(x) return x;
222 #endif
223 
224 /* must match constants in LogSyslogFast.pm */
225 #define LOG_UDP  0
226 #define LOG_TCP  1
227 #define LOG_UNIX 2
228 
229 int
LSF_set_receiver(LogSyslogFast * logger,int proto,const char * hostname,int port)230 LSF_set_receiver(LogSyslogFast* logger, int proto, const char* hostname, int port)
231 {
232     const struct sockaddr* p_address;
233     int address_len;
234 #ifdef AF_INET6
235     struct addrinfo* results = NULL;
236 #endif
237 
238     if (logger->sock >= 0) {
239         int ret = close(logger->sock);
240         if (ret) {
241             logger->err = strerror(errno);
242             return -1;
243         }
244     }
245 
246     /* set up a socket, letting kernel assign local port */
247     if (proto == LOG_UDP || proto == LOG_TCP) {
248 
249 #ifdef AF_INET6
250 
251 /* For NetBSD: http://www.mail-archive.com/bug-gnulib@gnu.org/msg17067.html */
252 #ifndef AI_ADDRCONFIG
253 #define AI_ADDRCONFIG 0
254 #endif
255 
256 /* For MacOS: http://mailman.videolan.org/pipermail/vlc-devel/2008-May/044005.html */
257 #ifndef AI_NUMERICSERV
258 #define AI_NUMERICSERV 0
259 #endif
260 
261         struct addrinfo *rp;
262         struct addrinfo hints;
263         char portstr[32];
264         int r;
265 
266         snprintf(portstr, sizeof(portstr), "%d", port);
267         memset(&hints, 0, sizeof(hints));
268         hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
269         hints.ai_family = AF_UNSPEC;
270         if (proto == LOG_TCP) {
271             hints.ai_socktype = SOCK_STREAM;
272         } else {
273             hints.ai_socktype = SOCK_DGRAM;
274         }
275         hints.ai_protocol = 0;
276         hints.ai_addrlen = 0;
277         hints.ai_addr = NULL;
278         hints.ai_canonname = NULL;
279         hints.ai_next = NULL;
280 
281         r = getaddrinfo(hostname, portstr, &hints, &results);
282         if (r < 0) {
283             logger->err = gai_strerror(r);
284             return -1;
285         }
286         else if (!results) {
287             logger->err = "no results from getaddrinfo";
288             return -1;
289         }
290         for (rp = results; rp != NULL; rp = rp->ai_next) {
291             logger->sock = socket(rp->ai_family, rp->ai_socktype, 0);
292             if (logger->sock == -1) {
293                 r = errno;
294                 continue;
295             }
296             p_address = rp->ai_addr;
297             address_len = rp->ai_addrlen;
298             break;
299         }
300         if (logger->sock == -1) {
301             logger->err = "socket failure";
302             clean_return(-1);
303         }
304 
305 #else /* !AF_INET6 */
306 
307         /* resolve the remote host */
308         struct hostent* host = gethostbyname(hostname);
309         if (!host || !host->h_addr_list || !host->h_addr_list[0]) {
310             logger->err = "resolve failure";
311             return -1;
312         }
313 
314         /* create the remote host's address */
315         struct sockaddr_in raddress;
316         raddress.sin_family = AF_INET;
317         memcpy(&raddress.sin_addr, host->h_addr_list[0], sizeof(raddress.sin_addr));
318         raddress.sin_port = htons(port);
319         p_address = (const struct sockaddr*) &raddress;
320         address_len = sizeof(raddress);
321 
322         /* construct socket */
323         if (proto == LOG_UDP) {
324             logger->sock = socket(AF_INET, SOCK_DGRAM, 0);
325 
326             /* make the socket non-blocking */
327             int flags = fcntl(logger->sock, F_GETFL, 0);
328             fcntl(logger->sock, F_SETFL, flags | O_NONBLOCK);
329             flags = fcntl(logger->sock, F_GETFL, 0);
330             if (!(flags & O_NONBLOCK)) {
331                 logger->err = "nonblock failure";
332                 return -1;
333             }
334         }
335         else if (proto == LOG_TCP) {
336             logger->sock = socket(AF_INET, SOCK_STREAM, 0);
337         }
338 
339 #endif /* AF_INET6 */
340     }
341     else if (proto == LOG_UNIX) {
342 
343         /* create the log device's address */
344         struct sockaddr_un raddress;
345         raddress.sun_family = AF_UNIX;
346         strncpy(raddress.sun_path, hostname, sizeof(raddress.sun_path) - 1);
347         p_address = (const struct sockaddr*) &raddress;
348         address_len = sizeof(raddress);
349 
350         /* construct socket */
351         logger->sock = socket(AF_UNIX, SOCK_STREAM, 0);
352     }
353     else {
354         logger->err = "bad protocol";
355         return -1;
356     }
357 
358     if (logger->sock < 0) {
359         logger->err = strerror(errno);
360         clean_return(-1);
361     }
362 
363     /* close the socket after exec to match normal Perl behavior for sockets */
364     fcntl(logger->sock, F_SETFD, FD_CLOEXEC);
365 
366     /* connect the socket */
367     if (connect(logger->sock, p_address, address_len) != 0) {
368         /* some servers (rsyslog) may use SOCK_DGRAM for unix domain sockets */
369         if (proto == LOG_UNIX && errno == EPROTOTYPE) {
370             /* clean up existing bad socket */
371             close(logger->sock);
372             if (logger->sock < 0) {
373                 logger->err = strerror(errno);
374                 clean_return(-1);
375             }
376 
377             logger->sock = socket(AF_UNIX, SOCK_DGRAM, 0);
378             if (connect(logger->sock, p_address, address_len) != 0) {
379                 logger->err = strerror(errno);
380                 clean_return(-1);
381             }
382         }
383         else {
384             logger->err = strerror(errno);
385             clean_return(-1);
386         }
387     }
388 
389     clean_return(0);
390 }
391 
392 int
LSF_send(LogSyslogFast * logger,const char * msg_str,int msg_len,time_t t)393 LSF_send(LogSyslogFast* logger, const char* msg_str, int msg_len, time_t t)
394 {
395     /* update the prefix if seconds have rolled over */
396     if (t != logger->last_time)
397         update_prefix(logger, t);
398 
399     int line_len = logger->prefix_len + msg_len;
400     /* ensure there's space in the buffer for total length including a trailing NULL */
401     if (logger->bufsize < line_len + 1) {
402         /* try to increase buffer */
403         int new_bufsize = 2 * logger->bufsize;
404         while (new_bufsize < line_len + 1)
405             new_bufsize *= 2;
406         if (new_bufsize < 0) {
407             /* overflow */
408             logger->err = "message too large";
409             return -1;
410         }
411 
412         char* new_buf = realloc(logger->linebuf, new_bufsize);
413         if (!new_buf) {
414             logger->err = strerror(errno);
415             return -1;
416         }
417 
418         logger->linebuf = new_buf;
419         logger->bufsize = new_bufsize;
420         logger->msg_start = logger->linebuf + logger->prefix_len;
421     }
422 
423     /* paste the message into linebuf just past where the prefix was placed */
424     memcpy(logger->msg_start, msg_str, msg_len + 1); /* include perl-added null */
425 
426     int ret = send(logger->sock, logger->linebuf, line_len, 0);
427 
428     if (ret < 0)
429         logger->err = strerror(errno);
430     return ret;
431 }
432 
433 int
LSF_get_priority(LogSyslogFast * logger)434 LSF_get_priority(LogSyslogFast* logger)
435 {
436     return logger->priority;
437 }
438 
439 int
LSF_get_facility(LogSyslogFast * logger)440 LSF_get_facility(LogSyslogFast* logger)
441 {
442     return logger->priority >> 3;
443 }
444 
445 int
LSF_get_severity(LogSyslogFast * logger)446 LSF_get_severity(LogSyslogFast* logger)
447 {
448     return logger->priority & 7;
449 }
450 
451 const char*
LSF_get_sender(LogSyslogFast * logger)452 LSF_get_sender(LogSyslogFast* logger)
453 {
454     return logger->sender;
455 }
456 
457 const char*
LSF_get_name(LogSyslogFast * logger)458 LSF_get_name(LogSyslogFast* logger)
459 {
460     return logger->name;
461 }
462 
463 int
LSF_get_pid(LogSyslogFast * logger)464 LSF_get_pid(LogSyslogFast* logger)
465 {
466     return logger->pid;
467 }
468 
469 int
LSF_get_sock(LogSyslogFast * logger)470 LSF_get_sock(LogSyslogFast* logger)
471 {
472     return logger->sock;
473 }
474 
475 int
LSF_get_format(LogSyslogFast * logger)476 LSF_get_format(LogSyslogFast* logger)
477 {
478     return logger->format;
479 }
480