1/***********************************************************************
2 * Copyright (c) 2009, Secure Endpoints Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * - Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in
14 *   the documentation and/or other materials provided with the
15 *   distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 **********************************************************************/
31
32/*
33 * Based on code by Alexander Yaworsky
34 */
35
36#include <config.h>
37
38#include <roken.h>
39
40#define SYSLOG_DGRAM_SIZE 1024
41
42static BOOL        syslog_opened = FALSE;
43
44static int         syslog_mask = 0xFF;
45static char        syslog_ident[ 128 ] = "";
46static int         syslog_facility = LOG_USER;
47static char        syslog_procid_str[ 20 ];
48
49static SOCKADDR_IN syslog_hostaddr;
50static SOCKET      syslog_socket = INVALID_SOCKET;
51static char        local_hostname[ MAX_COMPUTERNAME_LENGTH + 1 ];
52
53static char        syslog_hostname[ MAX_COMPUTERNAME_LENGTH + 1 ] = "localhost";
54static unsigned short syslog_port = SYSLOG_PORT;
55
56static int   datagramm_size;
57
58volatile BOOL initialized = FALSE;
59BOOL wsa_initialized = FALSE;
60CRITICAL_SECTION cs_syslog;
61
62ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
63init_syslog(const char * hostname)
64{
65    WSADATA wsd;
66    char * service;
67
68    if ( initialized )
69        return;
70
71    if( WSAStartup( MAKEWORD( 2, 2 ), &wsd ) ) {
72        fprintf(stderr, "Can't initialize WinSock\n");
73        /* we let the rest of the initialization code go through,
74           although none of the syslog calls would succeed. */
75    } else {
76        wsa_initialized = TRUE;
77    }
78
79    if (hostname)
80        strcpy_s(syslog_hostname, sizeof(syslog_hostname), hostname);
81    else
82        strcpy_s(syslog_hostname, sizeof(syslog_hostname), "");
83
84    service = strchr(syslog_hostname, ':');
85
86    if (service) {
87        int tp;
88
89        *service++ = '\0';
90
91        if ((tp = atoi(service)) <= 0) {
92            struct servent * se;
93
94            se = getservbyname(service, "udp");
95
96            syslog_port = (se == NULL)? SYSLOG_PORT: se->s_port;
97        } else {
98            syslog_port = (unsigned short) tp;
99        }
100    } else {
101        syslog_port = SYSLOG_PORT;
102    }
103
104    InitializeCriticalSection(&cs_syslog);
105    initialized = TRUE;
106
107    atexit(exit_syslog);
108}
109
110ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
111exit_syslog(void)
112{
113    if ( !initialized )
114        return;
115
116    closelog();
117
118    if ( wsa_initialized )
119        WSACleanup();
120
121    DeleteCriticalSection(&cs_syslog);
122    initialized = FALSE;
123}
124
125static void init_logger_addr()
126{
127    struct hostent * phe = NULL;
128
129    memset( &syslog_hostaddr, 0, sizeof(SOCKADDR_IN) );
130    syslog_hostaddr.sin_family = AF_INET;
131
132    if (syslog_hostname[0] == '\0')
133        goto use_default;
134
135    phe = gethostbyname( syslog_hostname );
136    if( !phe )
137        goto use_default;
138
139    memcpy( &syslog_hostaddr.sin_addr.s_addr, phe->h_addr, phe->h_length );
140
141    syslog_hostaddr.sin_port = htons( syslog_port );
142    return;
143
144use_default:
145    syslog_hostaddr.sin_addr.S_un.S_addr = htonl( 0x7F000001 );
146    syslog_hostaddr.sin_port = htons( SYSLOG_PORT );
147}
148
149/******************************************************************************
150 * closelog
151 *
152 * Close desriptor used to write to system logger.
153 */
154ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
155closelog()
156{
157    if ( !initialized )
158        return;
159
160    EnterCriticalSection(&cs_syslog);
161    if( syslog_opened ) {
162        closesocket( syslog_socket );
163        syslog_socket = INVALID_SOCKET;
164        syslog_opened = FALSE;
165    }
166    LeaveCriticalSection(&cs_syslog);
167}
168
169/******************************************************************************
170 * openlog
171 *
172 * Open connection to system logger.
173 */
174ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
175openlog( char* ident, int option, int facility )
176{
177    BOOL failed = FALSE;
178    SOCKADDR_IN sa_local;
179    DWORD n;
180    int size;
181
182    if ( !initialized )
183        return;
184
185    EnterCriticalSection(&cs_syslog);
186
187    if( syslog_opened )
188        goto done;
189
190    failed = TRUE;
191
192    syslog_facility = facility? facility : LOG_USER;
193
194    if( option & LOG_PID )
195        sprintf_s( syslog_procid_str, sizeof(syslog_procid_str), "[%lu]", GetCurrentProcessId() );
196    else
197        syslog_procid_str[0] = '\0';
198
199    /* FIXME: handle other options */
200
201    n = sizeof(local_hostname);
202    if( !GetComputerName( local_hostname, &n ) )
203        goto done;
204
205    syslog_socket = INVALID_SOCKET;
206
207    init_logger_addr();
208
209    for( n = 0;; n++ )
210    {
211        syslog_socket = socket( AF_INET, SOCK_DGRAM, 0 );
212        if( INVALID_SOCKET == syslog_socket )
213            goto done;
214
215        memset( &sa_local, 0, sizeof(SOCKADDR_IN) );
216        sa_local.sin_family = AF_INET;
217        if( bind( syslog_socket, (SOCKADDR*) &sa_local, sizeof(SOCKADDR_IN) ) == 0 )
218            break;
219        rk_closesocket( syslog_socket );
220        syslog_socket = INVALID_SOCKET;
221        if( n == 100 )
222            goto done;
223        Sleep(0);
224    }
225
226    /* get size of datagramm */
227    size = sizeof(datagramm_size);
228    if( getsockopt( syslog_socket, SOL_SOCKET, SO_MAX_MSG_SIZE, (char*) &datagramm_size, &size ) )
229        goto done;
230    if( datagramm_size - strlen(local_hostname) - (ident? strlen(ident) : 0) < 64 )
231        goto done;
232    if( datagramm_size > SYSLOG_DGRAM_SIZE )
233        datagramm_size = SYSLOG_DGRAM_SIZE;
234
235    if (ident)
236        strcpy_s(syslog_ident, sizeof(syslog_ident), ident);
237
238    syslog_facility = (facility ? facility : LOG_USER);
239    failed = FALSE;
240
241 done:
242    if( failed ) {
243        if( syslog_socket != INVALID_SOCKET )
244            rk_closesocket( syslog_socket );
245    }
246    syslog_opened = !failed;
247
248    LeaveCriticalSection(&cs_syslog);
249}
250
251/******************************************************************************
252 * setlogmask
253 *
254 * Set the log mask level.
255 */
256ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
257setlogmask( int mask )
258{
259    int ret;
260
261    if ( !initialized )
262        return 0;
263
264    EnterCriticalSection(&cs_syslog);
265
266    ret = syslog_mask;
267    if( mask )
268        syslog_mask = mask;
269
270    LeaveCriticalSection(&cs_syslog);
271
272    return ret;
273}
274
275/******************************************************************************
276 * syslog
277 *
278 * Generate a log message using FMT string and option arguments.
279 */
280ROKEN_LIB_FUNCTION void
281syslog( int pri, char* fmt, ... )
282{
283    va_list ap;
284
285    va_start( ap, fmt );
286    vsyslog( pri, fmt, ap );
287    va_end( ap );
288}
289
290/******************************************************************************
291 * vsyslog
292 *
293 * Generate a log message using FMT and using arguments pointed to by AP.
294 */
295ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
296vsyslog( int pri, char* fmt, va_list ap )
297{
298    static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
299                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
300    char  datagramm[ SYSLOG_DGRAM_SIZE ];
301    SYSTEMTIME stm;
302    int len;
303    char *p;
304
305    if ( !initialized )
306        return;
307
308    EnterCriticalSection(&cs_syslog);
309
310    if( !(LOG_MASK( LOG_PRI( pri )) & syslog_mask) )
311        goto done;
312
313    openlog( NULL, 0, pri & LOG_FACMASK );
314    if( !syslog_opened )
315        goto done;
316
317    if( !(pri & LOG_FACMASK) )
318        pri |= syslog_facility;
319
320    GetLocalTime( &stm );
321    len = sprintf_s( datagramm, sizeof(datagramm),
322                     "<%d>%s %2d %02d:%02d:%02d %s %s%s: ",
323                     pri,
324                     month[ stm.wMonth - 1 ], stm.wDay, stm.wHour, stm.wMinute, stm.wSecond,
325                     local_hostname, syslog_ident, syslog_procid_str );
326    vsprintf_s( datagramm + len, datagramm_size - len, fmt, ap );
327    p = strchr( datagramm, '\n' );
328    if( p )
329        *p = 0;
330    p = strchr( datagramm, '\r' );
331    if( p )
332        *p = 0;
333
334    sendto( syslog_socket, datagramm, strlen(datagramm), 0, (SOCKADDR*) &syslog_hostaddr, sizeof(SOCKADDR_IN) );
335
336 done:
337    LeaveCriticalSection(&cs_syslog);
338}
339
340