OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / user / rsyslog / tcpsrv.c
1 /* tcpsrv.c
2  *
3  * Common code for plain TCP based servers. This is currently being
4  * utilized by imtcp and imgssapi. I suspect that when we implement
5  * SSL/TLS, that module could also use tcpsrv.
6  *
7  * There are actually two classes within the tcpserver code: one is
8  * the tcpsrv itself, the other one is its sessions. This is a helper
9  * class to tcpsrv.
10  *
11  * The common code here calls upon specific functionality by using
12  * callbacks. The specialised input modules need to set the proper
13  * callbacks before the code is run. The tcpsrv then calls back
14  * into the specific input modules at the appropriate time.
15  *
16  * NOTE: read comments in module-template.h to understand how this file
17  *       works!
18  *
19  * File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
20  *
21  * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH.
22  *
23  * This file is part of rsyslog.
24  *
25  * Rsyslog is free software: you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License as published by
27  * the Free Software Foundation, either version 3 of the License, or
28  * (at your option) any later version.
29  *
30  * Rsyslog is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU General Public License
36  * along with Rsyslog.  If not, see <http://www.gnu.org/licenses/>.
37  *
38  * A copy of the GPL can be found in the file "COPYING" in this distribution.
39  */
40
41 #include "config.h"
42 #include <stdlib.h>
43 #include <assert.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <unistd.h>
47 #include <stdarg.h>
48 #include <ctype.h>
49 #include <netinet/in.h>
50 #include <netdb.h>
51 #include <sys/types.h>
52 #include <sys/socket.h>
53 #if HAVE_FCNTL_H
54 #include <fcntl.h>
55 #endif
56 #include "rsyslog.h"
57 #include "syslogd.h"
58 #include "cfsysline.h"
59 #include "module-template.h"
60 #include "net.h"
61 #include "srUtils.h"
62 #include "conf.h"
63 #include "tcpsrv.h"
64 #include "obj.h"
65 #include "errmsg.h"
66
67 MODULE_TYPE_LIB
68
69 /* defines */
70 #define TCPSESS_MAX_DEFAULT 200 /* default for nbr of tcp sessions if no number is given */
71
72 /* static data */
73 DEFobjStaticHelpers
74 DEFobjCurrIf(conf)
75 DEFobjCurrIf(tcps_sess)
76 DEFobjCurrIf(errmsg)
77 DEFobjCurrIf(net)
78
79
80
81 /* code to free all sockets within a socket table.
82  * A socket table is a descriptor table where the zero
83  * element has the count of elements. This is used for
84  * listening sockets. The socket table itself is also
85  * freed.
86  * A POINTER to this structure must be provided, thus
87  * double indirection!
88  * rgerhards, 2007-06-28
89  */
90 static void freeAllSockets(int **socks)
91 {
92         assert(socks != NULL);
93         assert(*socks != NULL);
94         while(**socks) {
95                 dbgprintf("Closing socket %d.\n", (*socks)[**socks]);
96                 close((*socks)[**socks]);
97                 (**socks)--;
98         }
99         free(*socks);
100         *socks = NULL;
101 }
102
103
104 /* configure TCP listener settings. This is called during command
105  * line parsing. The argument following -t is supplied as an argument.
106  * The format of this argument is
107  * "<port-to-use>, <nbr-of-sessions>"
108  * Typically, there is no whitespace between port and session number.
109  * (but it may be...).
110  * NOTE: you can not use dbgprintf() in here - the dbgprintf() system is
111  * not yet initilized when this function is called.
112  * rgerhards, 2007-06-21
113  * The port in cOptarg is handed over to us - the caller MUST NOT free it!
114  * rgerhards, 2008-03-20
115  */
116 static void
117 configureTCPListen(tcpsrv_t *pThis, char *cOptarg)
118 {
119         register int i;
120         register char *pArg;
121         register char *pNode = NULL;
122
123         assert(cOptarg != NULL);
124         ISOBJ_TYPE_assert(pThis, tcpsrv);
125
126         if(pThis->TCPLstnNode != NULL) {
127                 free(pThis->TCPLstnNode);
128                 pThis->TCPLstnNode = NULL;
129         }
130
131         pArg = strchr(cOptarg, ':');
132         if(pArg != NULL) {
133                 *pArg++ = '\0';
134                 pThis->TCPLstnNode = cOptarg;
135                 cOptarg = strdup(pArg);
136         }
137
138         /* extract port */
139         i = 0;
140         pArg = cOptarg;
141         while(isdigit((int) *pArg)) {
142                 i = i * 10 + *pArg++ - '0';
143         }
144
145         if(pThis->TCPLstnPort != NULL) {
146                 free(pThis->TCPLstnPort);
147                 pThis->TCPLstnPort = NULL;
148         }
149                 
150         if( i >= 0 && i <= 65535) {
151                 pThis->TCPLstnPort = cOptarg;
152         } else {
153                 free(cOptarg);
154                 errmsg.LogError(NO_ERRCODE, "Invalid TCP listen port %s - changed to 514.\n", cOptarg);
155         }
156 }
157
158
159 #if 0 // I think this is no longer needed
160 static void
161 configureTCPListenSessMax(char *cOptarg)
162 {
163         register int i;
164         register char *pArg = cOptarg;
165
166         assert(cOptarg != NULL);
167
168         /* number of sessions */
169         i = 0;
170         while(isdigit((int) *pArg)) {
171                 i = i * 10 + *pArg++ - '0';
172         }
173
174         if(i > 0)
175                 pThis->iSessMax = i;
176         else {
177                 /* too small, need to adjust */
178                 errmsg.LogError(NO_ERRCODE, "TCP session max configured to %s - changing to 1.\n", cOptarg);
179                 pThis->iSessMax = 1;
180         }
181 }
182 #endif
183
184
185 /* Initialize the session table
186  * returns 0 if OK, somewhat else otherwise
187  */
188 static rsRetVal
189 TCPSessTblInit(tcpsrv_t *pThis)
190 {
191         DEFiRet;
192
193         ISOBJ_TYPE_assert(pThis, tcpsrv);
194         assert(pThis->pSessions == NULL);
195
196         dbgprintf("Allocating buffer for %d TCP sessions.\n", pThis->iSessMax);
197         if((pThis->pSessions = (tcps_sess_t **) calloc(pThis->iSessMax, sizeof(tcps_sess_t *))) == NULL) {
198                 dbgprintf("Error: TCPSessInit() could not alloc memory for TCP session table.\n");
199                 ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
200         }
201
202 finalize_it:
203         RETiRet;
204 }
205
206
207 /* find a free spot in the session table. If the table
208  * is full, -1 is returned, else the index of the free
209  * entry (0 or higher).
210  */
211 static int
212 TCPSessTblFindFreeSpot(tcpsrv_t *pThis)
213 {
214         register int i;
215
216         ISOBJ_TYPE_assert(pThis, tcpsrv);
217
218         for(i = 0 ; i < pThis->iSessMax ; ++i) {
219                 if(pThis->pSessions[i] == NULL)
220                         break;
221         }
222
223         return((i < pThis->iSessMax) ? i : -1);
224 }
225
226
227 /* Get the next session index. Free session tables entries are
228  * skipped. This function is provided the index of the last
229  * session entry, or -1 if no previous entry was obtained. It
230  * returns the index of the next session or -1, if there is no
231  * further entry in the table. Please note that the initial call
232  * might as well return -1, if there is no session at all in the
233  * session table.
234  */
235 static int
236 TCPSessGetNxtSess(tcpsrv_t *pThis, int iCurr)
237 {
238         register int i;
239
240         ISOBJ_TYPE_assert(pThis, tcpsrv);
241         for(i = iCurr + 1 ; i < pThis->iSessMax ; ++i)
242                 if(pThis->pSessions[i] != NULL)
243                         break;
244
245         return((i < pThis->iSessMax) ? i : -1);
246 }
247
248
249 /* De-Initialize TCP listner sockets.
250  * This function deinitializes everything, including freeing the
251  * session table. No TCP listen receive operations are permitted
252  * unless the subsystem is reinitialized.
253  * rgerhards, 2007-06-21
254  */
255 static void deinit_tcp_listener(tcpsrv_t *pThis)
256 {
257         int iTCPSess;
258
259         ISOBJ_TYPE_assert(pThis, tcpsrv);
260         assert(pThis->pSessions != NULL);
261
262         /* close all TCP connections! */
263         iTCPSess = TCPSessGetNxtSess(pThis, -1);
264         while(iTCPSess != -1) {
265                 tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
266                 /* now get next... */
267                 iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
268         }
269         
270         /* we are done with the session table - so get rid of it...
271         */
272         free(pThis->pSessions);
273         pThis->pSessions = NULL; /* just to make sure... */
274
275         if(pThis->TCPLstnNode != NULL)
276                 free(pThis->TCPLstnNode);
277         if(pThis->TCPLstnPort != NULL)
278                 free(pThis->TCPLstnPort);
279
280         /* finally close the listen sockets themselfs */
281         freeAllSockets(&pThis->pSocksLstn);
282 }
283
284
285 /* Initialize TCP sockets (for listener)
286  * This function returns either NULL (which means it failed) or 
287  * a pointer to an array of file descriptiors. If the pointer is
288  * returned, the zeroest element [0] contains the count of valid
289  * descriptors. The descriptors themself follow in range
290  * [1] ... [num-descriptors]. It is guaranteed that each of these
291  * descriptors is valid, at least when this function returns.
292  * Please note that technically the array may be larger than the number
293  * of valid pointers stored in it. The memory overhead is minimal, so
294  * we do not bother to re-allocate an array of the exact size. Logically,
295  * the array still contains the exactly correct number of descriptors.
296  */
297 static int *create_tcp_socket(tcpsrv_t *pThis)
298 {
299         struct addrinfo hints, *res, *r;
300         int error, maxs, *s, *socks, on = 1;
301         char *TCPLstnPort;
302
303         ISOBJ_TYPE_assert(pThis, tcpsrv);
304
305         if(!strcmp(pThis->TCPLstnPort, "0"))
306                 TCPLstnPort = "514";
307                 /* use default - we can not do service db update, because there is
308                  * no IANA-assignment for syslog/tcp. In the long term, we might
309                  * re-use RFC 3195 port of 601, but that would probably break to
310                  * many existing configurations.
311                  * rgerhards, 2007-06-28
312                  */
313         else
314                 TCPLstnPort = pThis->TCPLstnPort;
315         dbgprintf("creating tcp socket on port %s\n", TCPLstnPort);
316         memset(&hints, 0, sizeof(hints));
317         hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
318         hints.ai_family = family;
319         hints.ai_socktype = SOCK_STREAM;
320
321         error = getaddrinfo(pThis->TCPLstnNode, TCPLstnPort, &hints, &res);
322         if(error) {
323                errmsg.LogError(NO_ERRCODE, "%s", gai_strerror(error));
324                return NULL;
325         }
326
327         /* Count max number of sockets we may open */
328         for (maxs = 0, r = res; r != NULL ; r = r->ai_next, maxs++)
329                 /* EMPTY */;
330         socks = malloc((maxs+1) * sizeof(int));
331         if (socks == NULL) {
332                errmsg.LogError(NO_ERRCODE, "couldn't allocate memory for TCP listen sockets, suspending TCP message reception.");
333                freeaddrinfo(res);
334                return NULL;
335         }
336
337         *socks = 0;   /* num of sockets counter at start of array */
338         s = socks + 1;
339         for (r = res; r != NULL ; r = r->ai_next) {
340                *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
341                 if (*s < 0) {
342                         if(!(r->ai_family == PF_INET6 && errno == EAFNOSUPPORT))
343                                 errmsg.LogError(NO_ERRCODE, "create_tcp_socket(), socket");
344                                 /* it is debatable if PF_INET with EAFNOSUPPORT should
345                                  * also be ignored...
346                                  */
347                         continue;
348                 }
349
350 #ifdef IPV6_V6ONLY
351                 if (r->ai_family == AF_INET6) {
352                         int iOn = 1;
353                         if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY,
354                               (char *)&iOn, sizeof (iOn)) < 0) {
355                         errmsg.LogError(NO_ERRCODE, "TCP setsockopt");
356                         close(*s);
357                         *s = -1;
358                         continue;
359                         }
360                 }
361 #endif
362                 if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR,
363                                (char *) &on, sizeof(on)) < 0 ) {
364                         errmsg.LogError(NO_ERRCODE, "TCP setsockopt(REUSEADDR)");
365                         close(*s);
366                         *s = -1;
367                         continue;
368                 }
369
370                 /* We need to enable BSD compatibility. Otherwise an attacker
371                  * could flood our log files by sending us tons of ICMP errors.
372                  */
373 #ifndef BSD     
374                 if(net.should_use_so_bsdcompat()) {
375                         if (setsockopt(*s, SOL_SOCKET, SO_BSDCOMPAT,
376                                         (char *) &on, sizeof(on)) < 0) {
377                                 errmsg.LogError(NO_ERRCODE, "TCP setsockopt(BSDCOMPAT)");
378                                 close(*s);
379                                 *s = -1;
380                                 continue;
381                         }
382                 }
383 #endif
384
385                 if( (bind(*s, r->ai_addr, r->ai_addrlen) < 0)
386 #ifndef IPV6_V6ONLY
387                      && (errno != EADDRINUSE)
388 #endif
389                    ) {
390                         errmsg.LogError(NO_ERRCODE, "TCP bind");
391                         close(*s);
392                         *s = -1;
393                         continue;
394                 }
395
396                 if( listen(*s,pThis->iSessMax / 10 + 5) < 0) {
397                         /* If the listen fails, it most probably fails because we ask
398                          * for a too-large backlog. So in this case we first set back
399                          * to a fixed, reasonable, limit that should work. Only if
400                          * that fails, too, we give up.
401                          */
402                         errmsg.LogError(NO_ERRCODE, "listen with a backlog of %d failed - retrying with default of 32.",
403                                     pThis->iSessMax / 10 + 5);
404                         if(listen(*s, 32) < 0) {
405                                 errmsg.LogError(NO_ERRCODE, "TCP listen, suspending tcp inet");
406                                 close(*s);
407                                 *s = -1;
408                                 continue;
409                         }
410                 }
411
412                 (*socks)++;
413                 s++;
414         }
415
416         if(res != NULL)
417                freeaddrinfo(res);
418
419         if(Debug && *socks != maxs)
420                 dbgprintf("We could initialize %d TCP listen sockets out of %d we received "
421                         "- this may or may not be an error indication.\n", *socks, maxs);
422
423         if(*socks == 0) {
424                 errmsg.LogError(NO_ERRCODE, "No TCP listen socket could successfully be initialized, "
425                          "message reception via TCP disabled.\n");
426                 free(socks);
427                 return(NULL);
428         }
429
430         /* OK, we had success. Now it is also time to
431          * initialize our connections
432          */
433         if(TCPSessTblInit(pThis) != 0) {
434                 /* OK, we are in some trouble - we could not initialize the
435                  * session table, so we can not continue. We need to free all
436                  * we have assigned so far, because we can not really use it...
437                  */
438                 errmsg.LogError(NO_ERRCODE, "Could not initialize TCP session table, suspending TCP message reception.");
439                 freeAllSockets(&socks); /* prevent a socket leak */
440                 return(NULL);
441         }
442
443         return(socks);
444 }
445
446
447 /* Accept new TCP connection; make entry in session table. If there
448  * is no more space left in the connection table, the new TCP
449  * connection is immediately dropped.
450  * ppSess has a pointer to the newly created session, if it succeds.
451  * If it does not succeed, no session is created and ppSess is
452  * undefined. If the user has provided an OnSessAccept Callback,
453  * this one is executed immediately after creation of the 
454  * session object, so that it can do its own initialization.
455  * rgerhards, 2008-03-02
456  */
457 static rsRetVal
458 SessAccept(tcpsrv_t *pThis, tcps_sess_t **ppSess, int fd)
459 {
460         DEFiRet;
461         tcps_sess_t *pSess;
462         int newConn;
463         int iSess = -1;
464         struct sockaddr_storage addr;
465         socklen_t addrlen = sizeof(struct sockaddr_storage);
466         uchar fromHost[NI_MAXHOST];
467         uchar fromHostFQDN[NI_MAXHOST];
468
469         ISOBJ_TYPE_assert(pThis, tcpsrv);
470
471         newConn = accept(fd, (struct sockaddr*) &addr, &addrlen);
472         if (newConn < 0) {
473                 errmsg.LogError(NO_ERRCODE, "tcp accept, ignoring error and connection request");
474                 ABORT_FINALIZE(RS_RET_ERR); // TODO: better error code
475                 //was: return -1;
476         }
477
478         /* Add to session list */
479         iSess = TCPSessTblFindFreeSpot(pThis);
480         if(iSess == -1) {
481                 errno = 0;
482                 errmsg.LogError(NO_ERRCODE, "too many tcp sessions - dropping incoming request");
483                 close(newConn);
484                 ABORT_FINALIZE(RS_RET_ERR); // TODO: better error code
485                 //was: return -1;
486         } else {
487                 /* we found a free spot and can construct our session object */
488                 CHKiRet(tcps_sess.Construct(&pSess));
489                 CHKiRet(tcps_sess.SetTcpsrv(pSess, pThis));
490         }
491
492         /* OK, we have a "good" index... */
493         /* get the host name */
494         if(net.cvthname(&addr, fromHost, fromHostFQDN) != RS_RET_OK) {
495                 /* we seem to have something malicous - at least we
496                  * are now told to discard the connection request.
497                  * Error message has been generated by cvthname.
498                  */
499                 close (newConn);
500                 ABORT_FINALIZE(RS_RET_ERR); // TODO: better error code
501                 //was: return -1;
502         }
503
504         /* Here we check if a host is permitted to send us
505          * syslog messages. If it isn't, we do not further
506          * process the message but log a warning (if we are
507          * configured to do this).
508          * rgerhards, 2005-09-26
509          */
510 RUNLOG_VAR("%p", ppSess);
511 RUNLOG_VAR("%p", pSess);
512         if(!pThis->pIsPermittedHost((struct sockaddr*) &addr, (char*) fromHostFQDN, pThis->pUsr, pSess->pUsr)) {
513                 dbgprintf("%s is not an allowed sender\n", (char *) fromHostFQDN);
514                 if(option_DisallowWarning) {
515                         errno = 0;
516                         errmsg.LogError(NO_ERRCODE, "TCP message from disallowed sender %s discarded",
517                                    (char*)fromHost);
518                 }
519                 close(newConn);
520                 ABORT_FINALIZE(RS_RET_HOST_NOT_PERMITTED);
521         }
522
523         /* OK, we have an allowed sender, so let's continue, what
524          * means we can finally fill in the session object.
525          */
526         CHKiRet(tcps_sess.SetHost(pSess, fromHost));
527         CHKiRet(tcps_sess.SetSock(pSess, newConn));
528         CHKiRet(tcps_sess.SetMsgIdx(pSess, 0));
529         CHKiRet(tcps_sess.ConstructFinalize(pSess));
530
531         /* check if we need to call our callback */
532         if(pThis->pOnSessAccept != NULL) {
533                 CHKiRet(pThis->pOnSessAccept(pThis, pSess));
534         }
535
536         *ppSess = pSess;
537         pThis->pSessions[iSess] = pSess;
538
539 finalize_it:
540         if(iRet != RS_RET_OK) {
541                 if(iSess != -1) {
542                         if(pThis->pSessions[iSess] != NULL)
543                                 tcps_sess.Destruct(&pThis->pSessions[iSess]);
544                 }
545                 iSess = -1; // TODO: change this to be fully iRet compliant ;)
546         }
547
548         RETiRet;
549 }
550
551
552 /* This function is called to gather input.
553  */
554 static rsRetVal
555 Run(tcpsrv_t *pThis)
556 {
557         DEFiRet;
558         int maxfds;
559         int nfds;
560         int i;
561         int iTCPSess;
562         fd_set readfds;
563         tcps_sess_t *pNewSess;
564
565         ISOBJ_TYPE_assert(pThis, tcpsrv);
566
567         /* this is an endless loop - it is terminated when the thread is
568          * signalled to do so. This, however, is handled by the framework,
569          * right into the sleep below.
570          */
571         while(1) {
572                 maxfds = 0;
573                 FD_ZERO (&readfds);
574
575                 /* Add the TCP listen sockets to the list of read descriptors.
576                  */
577                 if(pThis->pSocksLstn != NULL && *pThis->pSocksLstn) {
578                         for (i = 0; i < *pThis->pSocksLstn; i++) {
579                                 /* The if() below is theoretically not needed, but I leave it in
580                                  * so that a socket may become unsuable during execution. That
581                                  * feature is not yet supported by the current code base.
582                                  */
583                                 if (pThis->pSocksLstn[i+1] != -1) {
584                                         if(Debug)
585                                                 net.debugListenInfo(pThis->pSocksLstn[i+1], "TCP");
586                                         FD_SET(pThis->pSocksLstn[i+1], &readfds);
587                                         if(pThis->pSocksLstn[i+1]>maxfds) maxfds=pThis->pSocksLstn[i+1];
588                                 }
589                         }
590                         /* do the sessions */
591                         iTCPSess = TCPSessGetNxtSess(pThis, -1);
592                         while(iTCPSess != -1) {
593                                 int fdSess;
594                                 fdSess = pThis->pSessions[iTCPSess]->sock; // TODO: NOT CLEAN!, use method
595                                 dbgprintf("Adding TCP Session %d\n", fdSess);
596                                 FD_SET(fdSess, &readfds);
597                                 if (fdSess>maxfds) maxfds=fdSess;
598                                 /* now get next... */
599                                 iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
600                         }
601                 }
602
603                 if(Debug) {
604                         // TODO: name in dbgprintf!
605                         dbgprintf("--------<TCPSRV> calling select, active file descriptors (max %d): ", maxfds);
606                         for (nfds = 0; nfds <= maxfds; ++nfds)
607                                 if ( FD_ISSET(nfds, &readfds) )
608                                         dbgprintf("%d ", nfds);
609                         dbgprintf("\n");
610                 }
611
612                 /* wait for io to become ready */
613                 nfds = select(maxfds+1, (fd_set *) &readfds, NULL, NULL, NULL);
614
615                 for (i = 0; i < *pThis->pSocksLstn; i++) {
616                         if (FD_ISSET(pThis->pSocksLstn[i+1], &readfds)) {
617                                 dbgprintf("New connect on TCP inetd socket: #%d\n", pThis->pSocksLstn[i+1]);
618 RUNLOG_VAR("%p", &pNewSess);
619                                 SessAccept(pThis, &pNewSess, pThis->pSocksLstn[i+1]);
620                                 --nfds; /* indicate we have processed one */
621                         }
622                 }
623
624                 /* now check the sessions */
625                 iTCPSess = TCPSessGetNxtSess(pThis, -1);
626                 while(nfds && iTCPSess != -1) {
627                         int fdSess;
628                         int state;
629                         fdSess = pThis->pSessions[iTCPSess]->sock; // TODO: not clean, use method
630                         if(FD_ISSET(fdSess, &readfds)) {
631                                 char buf[MAXLINE];
632                                 dbgprintf("tcp session socket with new data: #%d\n", fdSess);
633
634                                 /* Receive message */
635                                 state = pThis->pRcvData(pThis->pSessions[iTCPSess], buf, sizeof(buf));
636                                 if(state == 0) {
637                                         pThis->pOnRegularClose(pThis->pSessions[iTCPSess]);
638                                         tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
639                                 } else if(state == -1) {
640                                         errmsg.LogError(NO_ERRCODE, "TCP session %d will be closed, error ignored\n", fdSess);
641                                         pThis->pOnErrClose(pThis->pSessions[iTCPSess]);
642                                         tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
643                                 } else {
644                                         /* valid data received, process it! */
645                                         if(tcps_sess.DataRcvd(pThis->pSessions[iTCPSess], buf, state) != RS_RET_OK) {
646                                                 /* in this case, something went awfully wrong.
647                                                  * We are instructed to terminate the session.
648                                                  */
649                                                 errmsg.LogError(NO_ERRCODE, "Tearing down TCP Session %d - see "
650                                                             "previous messages for reason(s)\n", iTCPSess);
651                                                 pThis->pOnErrClose(pThis->pSessions[iTCPSess]);
652                                                 tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
653                                         }
654                                 }
655                                 --nfds; /* indicate we have processed one */
656                         }
657                         iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
658                 }
659         }
660
661         RETiRet;
662 }
663
664
665 /* Standard-Constructor
666  */
667 BEGINobjConstruct(tcpsrv) /* be sure to specify the object type also in END macro! */
668         pThis->pSocksLstn = NULL;
669         pThis->iSessMax = 200; /* TODO: useful default ;) */
670 ENDobjConstruct(tcpsrv)
671
672
673 /* ConstructionFinalizer
674  */
675 static rsRetVal
676 tcpsrvConstructFinalize(tcpsrv_t __attribute__((unused)) *pThis)
677 {
678         DEFiRet;
679         ISOBJ_TYPE_assert(pThis, tcpsrv);
680         pThis->pSocksLstn = pThis->OpenLstnSocks(pThis);
681
682         RETiRet;
683 }
684
685
686 /* destructor for the tcpsrv object */
687 BEGINobjDestruct(tcpsrv) /* be sure to specify the object type also in END and CODESTART macros! */
688 CODESTARTobjDestruct(tcpsrv)
689         if(pThis->OnDestruct != NULL)
690                 pThis->OnDestruct(pThis->pUsr);
691
692         deinit_tcp_listener(pThis);
693 ENDobjDestruct(tcpsrv)
694
695
696 /* debugprint for the tcpsrv object */
697 BEGINobjDebugPrint(tcpsrv) /* be sure to specify the object type also in END and CODESTART macros! */
698 CODESTARTobjDebugPrint(tcpsrv)
699 ENDobjDebugPrint(tcpsrv)
700
701 /* set functions */
702 static rsRetVal
703 SetCBIsPermittedHost(tcpsrv_t *pThis, int (*pCB)(struct sockaddr *addr, char *fromHostFQDN, void*, void*))
704 {
705         DEFiRet;
706         pThis->pIsPermittedHost = pCB;
707         RETiRet;
708 }
709
710 static rsRetVal
711 SetCBRcvData(tcpsrv_t *pThis, int (*pRcvData)(tcps_sess_t*, char*, size_t))
712 {
713         DEFiRet;
714         pThis->pRcvData = pRcvData;
715         RETiRet;
716 }
717
718 static rsRetVal
719 SetCBOnListenDeinit(tcpsrv_t *pThis, int (*pCB)(void*))
720 {
721         DEFiRet;
722         pThis->pOnListenDeinit = pCB;
723         RETiRet;
724 }
725
726 static rsRetVal
727 SetCBOnSessAccept(tcpsrv_t *pThis, rsRetVal (*pCB)(tcpsrv_t*, tcps_sess_t*))
728 {
729         DEFiRet;
730         pThis->pOnSessAccept = pCB;
731         RETiRet;
732 }
733
734 static rsRetVal
735 SetCBOnDestruct(tcpsrv_t *pThis, rsRetVal (*pCB)(void*))
736 {
737         DEFiRet;
738         pThis->OnDestruct = pCB;
739         RETiRet;
740 }
741
742 static rsRetVal
743 SetCBOnSessConstructFinalize(tcpsrv_t *pThis, rsRetVal (*pCB)(void*))
744 {
745         DEFiRet;
746         pThis->OnSessConstructFinalize = pCB;
747         RETiRet;
748 }
749
750 static rsRetVal
751 SetCBOnSessDestruct(tcpsrv_t *pThis, rsRetVal (*pCB)(void*))
752 {
753         DEFiRet;
754         pThis->pOnSessDestruct = pCB;
755         RETiRet;
756 }
757
758 static rsRetVal
759 SetCBOnRegularClose(tcpsrv_t *pThis, rsRetVal (*pCB)(tcps_sess_t*))
760 {
761         DEFiRet;
762         pThis->pOnRegularClose = pCB;
763         RETiRet;
764 }
765
766 static rsRetVal
767 SetCBOnErrClose(tcpsrv_t *pThis, rsRetVal (*pCB)(tcps_sess_t*))
768 {
769         DEFiRet;
770         pThis->pOnErrClose = pCB;
771         RETiRet;
772 }
773
774 static rsRetVal
775 SetCBOpenLstnSocks(tcpsrv_t *pThis, int* (*pCB)(tcpsrv_t*))
776 {
777         DEFiRet;
778         pThis->OpenLstnSocks = pCB;
779         RETiRet;
780 }
781
782 static rsRetVal
783 SetUsrP(tcpsrv_t *pThis, void *pUsr)
784 {
785         DEFiRet;
786         pThis->pUsr = pUsr;
787         RETiRet;
788 }
789
790
791 /* queryInterface function
792  * rgerhards, 2008-02-29
793  */
794 BEGINobjQueryInterface(tcpsrv)
795 CODESTARTobjQueryInterface(tcpsrv)
796         if(pIf->ifVersion != tcpsrvCURR_IF_VERSION) { /* check for current version, increment on each change */
797                 ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
798         }
799
800         /* ok, we have the right interface, so let's fill it
801          * Please note that we may also do some backwards-compatibility
802          * work here (if we can support an older interface version - that,
803          * of course, also affects the "if" above).
804          */
805         pIf->DebugPrint = tcpsrvDebugPrint;
806         pIf->Construct = tcpsrvConstruct;
807         pIf->ConstructFinalize = tcpsrvConstructFinalize;
808         pIf->Destruct = tcpsrvDestruct;
809
810         pIf->SessAccept = SessAccept;
811         pIf->configureTCPListen = configureTCPListen;
812         pIf->create_tcp_socket = create_tcp_socket;
813         pIf->Run = Run;
814
815         pIf->SetUsrP = SetUsrP;
816         pIf->SetCBIsPermittedHost = SetCBIsPermittedHost;
817         pIf->SetCBOpenLstnSocks = SetCBOpenLstnSocks;
818         pIf->SetCBRcvData = SetCBRcvData;
819         pIf->SetCBOnListenDeinit = SetCBOnListenDeinit;
820         pIf->SetCBOnSessAccept = SetCBOnSessAccept;
821         pIf->SetCBOnSessConstructFinalize = SetCBOnSessConstructFinalize;
822         pIf->SetCBOnSessDestruct = SetCBOnSessDestruct;
823         pIf->SetCBOnDestruct = SetCBOnDestruct;
824         pIf->SetCBOnRegularClose = SetCBOnRegularClose;
825         pIf->SetCBOnErrClose = SetCBOnErrClose;
826
827 finalize_it:
828 ENDobjQueryInterface(tcpsrv)
829
830
831 /* exit our class
832  * rgerhards, 2008-03-10
833  */
834 BEGINObjClassExit(tcpsrv, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
835 CODESTARTObjClassExit(tcpsrv)
836         /* release objects we no longer need */
837         objRelease(tcps_sess, DONT_LOAD_LIB);
838         objRelease(conf, CORE_COMPONENT);
839         objRelease(errmsg, CORE_COMPONENT);
840         objRelease(net, LM_NET_FILENAME);
841 ENDObjClassExit(tcpsrv)
842
843
844 /* Initialize our class. Must be called as the very first method
845  * before anything else is called inside this class.
846  * rgerhards, 2008-02-29
847  */
848 BEGINObjClassInit(tcpsrv, 1, OBJ_IS_LOADABLE_MODULE) /* class, version - CHANGE class also in END MACRO! */
849         /* request objects we use */
850         CHKiRet(objUse(errmsg, CORE_COMPONENT));
851         CHKiRet(objUse(net, LM_NET_FILENAME));
852         CHKiRet(objUse(tcps_sess, DONT_LOAD_LIB));
853         CHKiRet(objUse(conf, CORE_COMPONENT));
854
855         /* set our own handlers */
856         OBJSetMethodHandler(objMethod_DEBUGPRINT, tcpsrvDebugPrint);
857         OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, tcpsrvConstructFinalize);
858 ENDObjClassInit(tcpsrv)
859
860
861 /* --------------- here now comes the plumbing that makes as a library module --------------- */
862
863
864 BEGINmodExit
865 CODESTARTmodExit
866         /* de-init in reverse order! */
867         tcpsrvClassExit();
868         tcps_sessClassExit();
869 ENDmodExit
870
871
872 BEGINqueryEtryPt
873 CODESTARTqueryEtryPt
874 CODEqueryEtryPt_STD_LIB_QUERIES
875 ENDqueryEtryPt
876
877
878 BEGINmodInit()
879 CODESTARTmodInit
880         *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
881
882         /* Initialize all classes that are in our module - this includes ourselfs */
883         CHKiRet(tcps_sessClassInit(pModInfo));
884         CHKiRet(tcpsrvClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
885 ENDmodInit
886
887 /* vim:set ai:
888  */