1 /* $CoreSDI: im_tcp.c,v 1.35 2002/03/01 07:31:02 alejo Exp $ */
2
3 /*
4 * Copyright (c) 2001, Core SDI S.A., Argentina
5 * All rights reserved
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither name of the Core SDI S.A. nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * im_tcp -- input from INET using TCP
34 *
35 * Author: Alejo Sanchez for Core SDI S.A.
36 *
37 * This input module is a bit tricky, because of the nature of TCP
38 * connections, and the use of poll() for I/O on syslogd
39 *
40 * The main idea is that first a im_tcp module will be called
41 * and it will bind to a port and wait for connections to it.
42 *
43 * Whenever a conection is established it will add it to an
44 * array of file descriptors of connections.
45 *
46 */
47
48 #include "config.h"
49
50
51 #if TIME_WITH_SYS_TIME
52 # include <sys/time.h>
53 # include <time.h>
54 #else
55 # if HAVE_SYS_TIME_H
56 # include <sys/time.h>
57 # else
58 # include <time.h>
59 # endif
60 #endif
61 #include <sys/types.h>
62 #include <sys/socket.h>
63 #include <sys/uio.h>
64 #include <ctype.h>
65 #include <stdlib.h>
66 #include <unistd.h>
67 #include <errno.h>
68 #include <signal.h>
69 #include <string.h>
70 #include <stdio.h>
71 #include <syslog.h>
72
73 #include "../modules.h"
74 #include "../syslogd.h"
75
76 /* recvfrom() and others like socklen_t, Irix doesn't provide it */
77 #ifndef HAVE_SOCKLEN_T
78
79 #endif
80
81 struct tcp_conn {
82 struct tcp_conn *next;
83 int fd;
84 char name[MAXHOSTNAMELEN + 1];
85 char port[20];
86 char saveline[MAXLINE + 3]; /* maxline + cr lf */
87 };
88
89 struct im_tcp_ctx {
90 socklen_t addrlen;
91 struct tcp_conn *first;
92 int flags;
93 };
94
95 #define M_USEMSGHOST 0x01
96 #define M_NOTFQDN 0x02
97
98
99 void printline(char *, char *, size_t, int);
100 int listen_tcp(char *host, char *port, socklen_t *);
101 int accept_tcp(int, socklen_t, char *, int, char *, int);
102
103
104 /*
105 * initialize tcp input
106 *
107 * this module takes a host argument (ie. 0.0.0.0, 0::0, server.example.com)
108 * and a port/service ('syslog' or numerical)
109 *
110 */
111
112 int
im_tcp_init(struct i_module * I,char ** argv,int argc)113 im_tcp_init(struct i_module *I, char **argv, int argc)
114 {
115 struct im_tcp_ctx *c;
116 char *host, *port;
117 int ch, argcnt;
118
119 dprintf(MSYSLOG_INFORMATIVE, "im_tcp_init: entering\n");
120
121 if ( (I->im_ctx = calloc(1, sizeof(struct im_tcp_ctx))) == NULL) {
122 dprintf(MSYSLOG_SERIOUS, "om_tcp_init: cant alloc memory");
123 return (-1);
124 }
125
126 c = (struct im_tcp_ctx *) I->im_ctx;
127
128 host = "0.0.0.0";
129 port = "syslog";
130
131 argcnt = 1; /* skip module name */
132
133 while ((ch = getxopt(argc, argv, "h!host: p!port: a!addhost q!nofqdn",
134 &argcnt)) != -1) {
135
136 switch (ch) {
137 case 'h':
138 /* get addr to bind */
139 host = argv[argcnt];
140 break;
141 case 'p':
142 /* get remote host port */
143 port = argv[argcnt];
144 break;
145 case 'a':
146 c->flags |= M_USEMSGHOST;
147 break;
148 case 'q':
149 /* don't use domain in hostname (FQDN) */
150 c->flags |= M_NOTFQDN;
151 break;
152 default:
153 dprintf(MSYSLOG_SERIOUS, "om_tcp_init: parsing error"
154 " [%c]\n", ch);
155 free(c);
156 return (-1);
157 }
158 argcnt++;
159 }
160
161 if ( (I->im_fd = listen_tcp(host, port, &c->addrlen)) < 0) {
162 dprintf(MSYSLOG_SERIOUS, "im_tcp_init: error with listen_tcp() %s\n",
163 strerror(errno));
164 free(c);
165 return (-1);
166 }
167
168 I->im_path = NULL;
169
170 add_fd_input(I->im_fd , I);
171
172 dprintf(MSYSLOG_INFORMATIVE, "im_tcp_init: running\n");
173
174 return (1);
175 }
176
177
178 /*
179 * im_tcp_read: accept a connection and add it to the queue
180 *
181 */
182
183 int
im_tcp_read(struct i_module * im,int infd,struct im_msg * ret)184 im_tcp_read(struct i_module *im, int infd, struct im_msg *ret)
185 {
186 struct im_tcp_ctx *c;
187 struct tcp_conn *con;
188 int n;
189
190 dprintf(MSYSLOG_INFORMATIVE, "im_tcp_read: entering...\n");
191
192 if (im == NULL || ret == NULL) {
193 dprintf(MSYSLOG_SERIOUS, "im_tcp_read: arg %s%s is null\n",
194 ret? "ret":"", im? "im" : "");
195 return (-1);
196 }
197
198 if ((c = (struct im_tcp_ctx *) im->im_ctx) == NULL) {
199 dprintf(MSYSLOG_SERIOUS, "im_tcp_read: null context\n");
200 return (-1);
201 }
202
203 if (infd == im->im_fd) {
204
205 dprintf(MSYSLOG_INFORMATIVE, "im_tcp_read: new connection\n");
206
207 /* create a new connection */
208 if ((con = (struct tcp_conn *) calloc(1, sizeof(*con)))
209 == NULL) {
210 dprintf(MSYSLOG_SERIOUS, "im_tcp_read: "
211 "error allocating conn struct\n");
212 return (-1);
213 }
214
215 /* accept it and add to queue */
216 if ((con->fd = accept_tcp(infd, c->addrlen, con->name,
217 sizeof(con->name), con->port, sizeof(con->port))) < 0) {
218 dprintf(MSYSLOG_SERIOUS, "im_tcp_read: couldn't accept\n");
219 free (con);
220 return (-1);
221 }
222
223 /* add to queue */
224 con->next = c->first;
225 c->first = con;
226
227
228 dprintf(MSYSLOG_INFORMATIVE, "im_tcp_read: new conection from"
229 " %s with fd %d\n", con->name, con->fd);
230
231 /* add to inputs list */
232 add_fd_input(con->fd , im);
233
234 return (0); /* 0 because there is no line to log */
235
236 }
237
238 /* read connected socket */
239
240 dprintf(MSYSLOG_INFORMATIVE, "im_tcp_read: reading connection fd %d\n",
241 infd);
242
243 /* find connection */
244 for (con = c->first; con && con->fd != infd; con = con->next);
245
246 if (con == NULL || con->fd != infd) {
247 dprintf(MSYSLOG_SERIOUS, "im_tcp_read: no such connection "
248 "fd %d !\n", infd);
249 remove_fd_input(infd);
250 return (-1);
251 }
252
253 n = read(con->fd, im->im_buf, sizeof(im->im_buf) - 1);
254 if (n == 0) {
255 struct tcp_conn **prev;
256
257 dprintf(MSYSLOG_INFORMATIVE, "im_tcp_read: conetion from %s"
258 " closed\n", con->name);
259
260 remove_fd_input(con->fd);
261
262 /* connection closed, remove its tcp_con struct */
263 close (con->fd);
264
265 /* remove node */
266 for (prev = &c->first; *prev != NULL ; prev = &(*prev)->next) {
267
268 if (*prev == con) {
269
270 *prev = con->next;
271 break;
272 }
273 }
274
275 if (con->saveline[0] != '\0')
276 printline(ret->im_host, con->saveline,
277 strlen(con->saveline), 0);
278
279 free(con);
280
281 return (0);
282
283 } else if (n < 0 && errno != EINTR) {
284 dprintf(MSYSLOG_INFORMATIVE, "im_tcp_read: conetion from %s"
285 " closed with error [%s]\n", con->name, strerror(errno));
286 logerror("im_tcp_read");
287 con->fd = -1;
288 remove_fd_input(con->fd);
289 return (0);
290 } else {
291 char *p, *nextline, *cr;
292
293 /* terminate it */
294 (im->im_buf)[n] = '\0';
295 p = &im->im_buf[0];
296
297 dprintf(MSYSLOG_INFORMATIVE, "im_tcp_read: read: %s [%s]",
298 con->name, im->im_buf);
299
300 /* change non printable chars to X, just in case */
301 for(p = im->im_buf; *p != '\0'; p++)
302 if (!isprint((unsigned int) *p) && *p != '\n')
303 *p = 'X';
304 p = im->im_buf;
305
306 do {
307 char *msg;
308
309 msg = p;
310
311 /* multiple lines ? */
312 if((nextline = strchr(p, '\n')) != NULL) {
313 /* terminate this line and advance */
314 *nextline++ = '\0';
315 if (*nextline == '\0')
316 nextline = NULL; /* no more lines */
317 } else {
318 /* save this partial line and return */
319 strncat(con->saveline, p,
320 sizeof(con->saveline) - 1
321 - strlen(con->saveline));
322 }
323
324 /* remove trailing carriage returns */
325 if ((cr = strchr(p, '\r')) != NULL)
326 *cr = '\0';
327
328 if (*p == '\0') {
329 if (nextline != NULL) {
330 p = nextline;
331 continue;
332 } else
333 return (0);
334 }
335
336 if (c->flags & M_USEMSGHOST) {
337 char host[90];
338 int n1, n2;
339
340 if (con->saveline[0] != '\0') {
341 strncat(con->saveline, p,
342 sizeof(con->saveline) - 1
343 - strlen(con->saveline));
344 msg = con->saveline;
345 } else {
346 msg = p;
347 }
348
349 /* extract hostname from message */
350 if ((sscanf(msg, "<%*d>%*3s %*i %*i:%*i:%*i %n%89s"
351 " %n", &n1, host, &n2) != 1 &&
352 sscanf(msg, "%*3s %*i %*i:%*i:%*i %n%89s %n",
353 &n1, host, &n2) != 1 &&
354 sscanf(msg, "%n%89s %n", &n1,
355 host, &n2) != 1)
356 || im->im_buf[n2] == '\0') {
357 dprintf(MSYSLOG_INFORMATIVE,
358 "im_tcp_read: ignoring invalid "
359 "message [%s]\n", msg);
360 if (nextline != NULL) {
361 p = nextline;
362 continue;
363 } else {
364 return (0);
365 con->saveline[0] = '\0';
366 }
367 }
368
369 /* remove host from message */
370 while (im->im_buf[n2] != '\0')
371 im->im_buf[n1++] = im->im_buf[n2++];
372 im->im_buf[n1] = '\0';
373
374 strncpy(ret->im_host, host,
375 sizeof(ret->im_host) - 1);
376 ret->im_host[sizeof(ret->im_host) - 1] = '\0';
377
378 } else {
379
380 /* get hostname from originating addr */
381 strncpy(ret->im_host, con->name,
382 sizeof(ret->im_host) - 1);
383 ret->im_host[sizeof(ret->im_host) - 1] = '\0';
384 }
385
386 if (c->flags & M_NOTFQDN) {
387 char *dot;
388
389 if ((dot = strchr(ret->im_host, '.')) != NULL)
390 *dot = '\0';
391 }
392
393 printline(ret->im_host, msg, strlen(msg), 0);
394 *msg = '\0';
395
396 p = nextline;
397
398 } while (nextline != NULL);
399 }
400
401 return (0); /* we already logged the lines */
402 }
403
404 int
im_tcp_close(struct i_module * im)405 im_tcp_close(struct i_module *im)
406 {
407 struct im_tcp_ctx *c;
408 struct tcp_conn *con, *cnext;
409
410 c = (struct im_tcp_ctx *) im->im_ctx;
411
412 /* close all connections */
413 for (con = c->first; con; con = cnext) {
414 if (con->fd > -1)
415 close(con->fd);
416 cnext = con->next;
417 free(con);
418 }
419
420 im->im_ctx = NULL;
421
422 /* close listening socket */
423 return (close(im->im_fd));
424 }
425