00001 /* 00002 * Copyright (c) 2002 - 2003 00003 * NetGroup, Politecnico di Torino (Italy) 00004 * All rights reserved. 00005 * 00006 * Redistribution and use in source and binary forms, with or without 00007 * modification, are permitted provided that the following conditions 00008 * are met: 00009 * 00010 * 1. Redistributions of source code must retain the above copyright 00011 * notice, this list of conditions and the following disclaimer. 00012 * 2. Redistributions in binary form must reproduce the above copyright 00013 * notice, this list of conditions and the following disclaimer in the 00014 * documentation and/or other materials provided with the distribution. 00015 * 3. Neither the name of the Politecnico di Torino nor the names of its 00016 * contributors may be used to endorse or promote products derived from 00017 * this software without specific prior written permission. 00018 * 00019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00020 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00021 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 00022 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 00023 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00024 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00025 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00026 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00027 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00029 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00030 * 00031 */ 00032 00033 00034 00035 00036 00037 #include <errno.h> // for the errno variable 00038 #include <string.h> // for strtok, etc 00039 #include <stdlib.h> // for malloc(), free(), ... 00040 #include <pcap.h> // for PCAP_ERRBUF_SIZE 00041 #include <signal.h> // for signal() 00042 #include <pthread.h> 00043 #include "rpcapd.h" 00044 #include "fileconf.h" // for the configuration file management 00045 #include "pcap-remote.h" 00046 #include "daemon.h" // the true main() method of this daemon 00047 #include "utils.h" // Missing calls and such 00048 #include "sockutils.h" // for socket calls 00049 00050 #ifndef WIN32 00051 #include <unistd.h> // for exit() 00052 #include <sys/wait.h> // waitpid() 00053 #else 00054 #include "win32-svc.h" // for Win32 service stuff 00055 #endif 00056 00057 00058 // Global variables 00059 char hostlist[MAX_HOST_LIST + 1]; 00060 struct active_pars activelist[MAX_ACTIVE_LIST]; 00061 int nullAuthAllowed; 00062 SOCKET sockmain; 00063 char loadfile[MAX_LINE + 1]; 00064 int passivemode= 1; 00065 struct addrinfo mainhints; 00066 char address[MAX_LINE + 1]; 00067 char port[MAX_LINE + 1]; 00068 00069 extern char *optarg; // for getopt() 00070 00071 00072 00073 // Function definition 00074 void main_passive(void *ptr); 00075 void main_active(void *ptr); 00076 00077 00078 #ifndef WIN32 00079 void main_cleanup_childs(int sign); 00080 #endif 00081 00082 00086 void printusage() 00087 { 00088 char *usagetext = 00089 "USAGE:\n" 00090 " " PROGRAM_NAME " [-b <address>] [-p <port>] [-6] [-l <host_list>] [-a <host,port>]\n" 00091 " [-n] [-v] [-d] [-s <file>] [-f <file>]\n" 00092 " -b <address>: the address to bind to (either numeric or literal).\n" 00093 " Default: it binds to all local IPv4 addresses\n" 00094 " -p <port>: the port to bind to. Default: it binds to port " RPCAP_DEFAULT_NETPORT "\n" 00095 " -4: use only IPv4 (default both IPv4 and IPv6 waiting sockets are used)\n" 00096 " -l <host_list>: a file that keeps the list of the hosts which\n" 00097 " are allowed to connect to this server (if more than one,\n" 00098 " list them one per line). We suggest to use \n" 00099 " literal names (instead of numeric ones) in order to avoid\n" 00100 " problems with different address families\n" 00101 " -n: permit NULL authentication (usually used with '-l')\n" 00102 " -a <host, port>: run in active mode when connecting to 'host' on port 'port'\n" 00103 " -v: run in active mode only (default: if '-a' is specified, it accepts passive\n" 00104 " connections as well\n" 00105 " -d: run in daemon mode (UNIX only) or as a service (Win32 only)\n" 00106 " Warning (Win32): this switch is provided automatically when the service\n" 00107 " is started from the control panel\n" 00108 " -s <file>: save the current configuration to file\n" 00109 " -f <file>: load the current configuration from file; all the switches\n" 00110 " specified from the command line are ignored\n" 00111 " -h: print this help screen\n\n"; 00112 00113 printf(usagetext); 00114 } 00115 00116 00117 00119 int main(int argc, char *argv[], char *envp[]) 00120 { 00121 char savefile[MAX_LINE + 1]; // name of the file on which we have to save the configuration 00122 int isdaemon= 0; // Not null if the user wants to run this program as a daemon 00123 int retval; // keeps the returning value from several functions 00124 char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed 00125 00126 00127 savefile[0]= 0; 00128 loadfile[0]= 0; 00129 hostlist[0]= 0; 00130 00131 // Initialize errbuf 00132 memset(errbuf, 0, sizeof(errbuf) ); 00133 00134 if (sock_init(errbuf, PCAP_ERRBUF_SIZE) == -1) 00135 { 00136 SOCK_ASSERT(errbuf, 1); 00137 exit(-1); 00138 } 00139 00140 strncpy(address, RPCAP_DEFAULT_NETADDR, MAX_LINE); 00141 strncpy(port, RPCAP_DEFAULT_NETPORT, MAX_LINE); 00142 00143 // Prepare to open a new server socket 00144 memset(&mainhints, 0, sizeof(struct addrinfo)); 00145 00146 mainhints.ai_family = PF_UNSPEC; 00147 mainhints.ai_flags = AI_PASSIVE; // Ready to a bind() socket 00148 mainhints.ai_socktype = SOCK_STREAM; 00149 00150 // Getting the proper command line options 00151 while ((retval = getopt(argc, argv, "b:dhp:4l:na:s:f:v")) != -1) 00152 { 00153 switch (retval) 00154 { 00155 case 'b': 00156 strncpy(address, optarg, MAX_LINE); 00157 break; 00158 case 'p': 00159 strncpy(port, optarg, MAX_LINE); 00160 break; 00161 case '4': 00162 mainhints.ai_family = PF_INET; // IPv4 server only 00163 break; 00164 case 'd': 00165 isdaemon= 1; 00166 break; 00167 case 'n': 00168 nullAuthAllowed= 1; 00169 break; 00170 case 'v': 00171 passivemode= 0; 00172 break; 00173 case 'l': 00174 { 00175 strncpy(hostlist, optarg, sizeof(hostlist) ); 00176 break; 00177 } 00178 case 'a': 00179 { 00180 char *tmpaddress, *tmpport; 00181 int i= 0; 00182 00183 tmpaddress= strtok(optarg, RPCAP_HOSTLIST_SEP); 00184 00185 while ( (tmpaddress != NULL) && (i < MAX_ACTIVE_LIST) ) 00186 { 00187 tmpport= strtok(NULL, RPCAP_HOSTLIST_SEP); 00188 00189 snprintf(activelist[i].address, MAX_LINE, tmpaddress); 00190 00191 if ( (tmpport == NULL) || (strcmp(tmpport, "DEFAULT") == 0) ) // the user choose a custom port 00192 snprintf(activelist[i].port, MAX_LINE, RPCAP_DEFAULT_NETPORT_ACTIVE); 00193 else 00194 snprintf(activelist[i].port, MAX_LINE, tmpport); 00195 00196 tmpaddress = strtok(NULL, RPCAP_HOSTLIST_SEP); 00197 00198 i++; 00199 } 00200 00201 if (i > MAX_ACTIVE_LIST) 00202 SOCK_ASSERT("Only MAX_ACTIVE_LIST active connections are currently supported.", 1); 00203 00204 // I don't initialize the remaining part of the structure, since 00205 // it is already zeroed (it is a global var) 00206 break; 00207 } 00208 case 'f': 00209 strncpy(loadfile, optarg, MAX_LINE); 00210 break; 00211 case 's': 00212 strncpy(savefile, optarg, MAX_LINE); 00213 break; 00214 case 'h': 00215 printusage(); 00216 exit(0); 00217 default: 00218 break; 00219 } 00220 } 00221 00222 if (savefile[0]) 00223 { 00224 if (fileconf_save(savefile) ) 00225 SOCK_ASSERT("Error when saving the configuration to file", 1); 00226 } 00227 00228 // If the file does not exist, it keeps the settings provided by the command line 00229 if (loadfile[0]) 00230 fileconf_read(0); 00231 00232 #ifdef linux 00233 // SIGTERM (i.e. kill -15) is not generated in WIN32, although it is included for ANSI compatibility 00234 signal(SIGTERM, main_cleanup); 00235 signal(SIGCHLD, main_cleanup_childs); 00236 #endif 00237 00238 // forking a daemon, if it is needed 00239 if (isdaemon) 00240 { 00241 #ifndef WIN32 00242 int pid; 00243 00244 // Unix Network Programming, pg 336 00245 if ( (pid = fork() ) != 0) 00246 exit(0); // Parent terminates 00247 00248 // First child continues 00249 // Set daemon mode 00250 setsid(); 00251 00252 // generated under unix with 'kill -HUP', needed to reload the configuration 00253 signal(SIGHUP, fileconf_read); 00254 00255 if ( (pid = fork() ) != 0) 00256 exit(0); // First child terminates 00257 00258 // LINUX WARNING: the current linux implementation of pthreads requires a management thread 00259 // to handle some hidden stuff. So, as soon as you create the first thread, two threads are 00260 // created. Fom this point on, the number of threads active are always one more compared 00261 // to the number you're expecting 00262 00263 // Second child continues 00264 // umask(0); 00265 // chdir("/"); 00266 #else 00267 // We use the SIGABRT signal to kill the Win32 service 00268 signal(SIGABRT, main_cleanup); 00269 00270 // If this call succeeds, it is blocking on Win32 00271 if ( svc_start() != 1) 00272 SOCK_ASSERT(1, "Unable to start the service"); 00273 00274 // When the previous call returns, the entire application has to be stopped. 00275 exit(0); 00276 #endif 00277 } 00278 else // Console mode 00279 { 00280 // Enable the catching of Ctrl+C 00281 signal(SIGINT, main_cleanup); 00282 00283 #ifndef WIN32 00284 // generated under unix with 'kill -HUP', needed to reload the configuration 00285 // We do not have this kind of signal in Win32 00286 signal(SIGHUP, fileconf_read); 00287 #endif 00288 00289 printf("Press CTRL + C to stop the server...\n"); 00290 } 00291 00292 // If we're a Win32 service, we have already called this function in the service_main 00293 main_startup(); 00294 00295 // The code should never arrive here (since the main_startup is blocking) 00296 // however this avoids a compiler warning 00297 exit(0); 00298 } 00299 00300 00301 00302 void main_startup(void) 00303 { 00304 char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed 00305 struct addrinfo *addrinfo; // keeps the addrinfo chain; required to open a new socket 00306 int i; 00307 #ifdef WIN32 00308 pthread_t threadId; // Pthread variable that keeps the thread structures 00309 #else 00310 pid_t pid; 00311 #endif 00312 00313 i= 0; 00314 addrinfo= NULL; 00315 memset(errbuf, 0, sizeof(errbuf) ); 00316 00317 // Starts all the active threads 00318 while ( (activelist[i].address[0] != 0) && (i < MAX_ACTIVE_LIST) ) 00319 { 00320 activelist[i].ai_family= mainhints.ai_family; 00321 00322 #ifdef WIN32 00323 if ( pthread_create( &threadId, NULL, (void *) &main_active, (void *) &activelist[i]) ) 00324 { 00325 SOCK_ASSERT("Error creating the active child thread", 1); 00326 continue; 00327 } 00328 #else 00329 if ( (pid= fork() ) == 0) // I am the child 00330 { 00331 main_active( (void *) &activelist[i]); 00332 exit(0); 00333 } 00334 #endif 00335 i++; 00336 } 00337 00338 /* 00339 The code that manages the active connections is not blocking; 00340 vice versa, the code that manages the passive connection is blocking. 00341 So, if the user do not want to run in passive mode, we have to block 00342 the main thread here, otherwise the program ends and all threads 00343 are stopped. 00344 00345 WARNING: this means that in case we have only active mode, the program does 00346 not terminate even if all the child thread terminates. The user has always to 00347 press Ctrl+C (or send a SIGTERM) to terminate the program. 00348 */ 00349 00350 if (passivemode) 00351 { 00352 struct addrinfo *tempaddrinfo; 00353 00354 // Do the work 00355 if (sock_initaddress((address[0]) ? address : NULL, port, &mainhints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1) 00356 { 00357 SOCK_ASSERT(errbuf, 1); 00358 return; 00359 } 00360 00361 tempaddrinfo= addrinfo; 00362 00363 while (tempaddrinfo) 00364 { 00365 if ( (sockmain= sock_open(tempaddrinfo, SOCKOPEN_SERVER, SOCKET_MAXCONN, errbuf, PCAP_ERRBUF_SIZE)) == -1) 00366 { 00367 SOCK_ASSERT(errbuf, 1); 00368 tempaddrinfo= tempaddrinfo->ai_next; 00369 continue; 00370 } 00371 00372 #ifdef WIN32 00373 if ( pthread_create( &threadId, NULL, (void *) &main_passive, (void *) &sockmain ) ) 00374 { 00375 SOCK_ASSERT("Error creating the passive child thread", 1); 00376 continue; 00377 } 00378 #else 00379 if ( (pid= fork() ) == 0) // I am the child 00380 { 00381 main_passive( (void *) &sockmain); 00382 return; 00383 } 00384 #endif 00385 tempaddrinfo= tempaddrinfo->ai_next; 00386 } 00387 00388 freeaddrinfo(addrinfo); 00389 } 00390 00391 // All the previous calls are no blocking, so the main line of execution goes here 00392 // and I have to avoid that the program terminates 00393 while (1) 00394 pthread_suspend(10*60*1000); // it wakes up every 10 minutes; it seems to me reasonable 00395 } 00396 00397 00398 /* 00399 \brief Closes gracefully (more or less) the program. 00400 00401 This function is called: 00402 - when we're running in console 00403 - when we're running as a Win32 service (in case we press STOP) 00404 00405 It is not called when we are running as a daemon on UNIX, since 00406 we do not define a signal in order to terminate gracefully the daemon. 00407 00408 This function makes a fast cleanup (it does not clean everything, as 00409 you can see from the fact that it uses kill() on UNIX), closes 00410 the main socket, free winsock resources (on Win32) and exits the 00411 program. 00412 */ 00413 void main_cleanup(int sign) 00414 { 00415 #ifndef WIN32 00416 // Sends a KILL signal to all the processes 00417 // that share the same process group (i.e. kills all the childs) 00418 kill(0, SIGKILL); 00419 #endif 00420 00421 SOCK_ASSERT(PROGRAM_NAME " is closing.\n", 1); 00422 00423 if (sockmain) 00424 closesocket(sockmain); 00425 sock_cleanup(); 00426 00427 /* 00428 This code is executed under the following conditions: 00429 - SIGTERM: we're under UNIX, and the user kills us with 'kill -15' 00430 (no matter is we're a daemon or in a console mode) 00431 - SIGINT: we're in console mode and the user sends us a Ctrl+C 00432 (SIGINT signal), no matter if we're UNIX or Win32 00433 00434 In all these cases, we have to terminate the program. 00435 The case that still remains is if we're a Win32 service: in this case, 00436 we're a child thread, and we want just to terminate ourself. This is because 00437 the exit(0) will be invoked by the main thread, which is blocked waiting that 00438 all childs terminates. We are forced to call exit from the main thread otherwise 00439 the Win32 service control manager (SCM) does not work well. 00440 */ 00441 if ( (sign == SIGTERM) || (sign == SIGINT) ) 00442 exit(0); 00443 else 00444 return; 00445 } 00446 00447 00448 00449 #ifdef linux 00450 00451 void main_cleanup_childs(int sign) 00452 { 00453 pid_t pid; 00454 int stat; 00455 00456 // For reference, Stevens, pg 128 00457 00458 while ( (pid= waitpid(-1, &stat, WNOHANG) ) > 0) 00459 SOCK_ASSERT("Child terminated", 1); 00460 00461 return; 00462 } 00463 00464 #endif 00465 00466 00467 00468 00469 00483 void main_passive(void *ptr) 00484 { 00485 char fakeerrbuf[PCAP_ERRBUF_SIZE + 1]; // needed to keep the message due to an error that we want to discard. 00486 char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed 00487 SOCKET sockctrl; // keeps the socket ID for this control connection 00488 struct sockaddr_storage from; // generic sockaddr_storage variable 00489 socklen_t fromlen; // keeps the length of the sockaddr_storage variable 00490 SOCKET sockmain; 00491 00492 #ifndef WIN32 00493 pid_t pid; 00494 #endif 00495 00496 sockmain= *((SOCKET *) ptr); 00497 // Initialize errbuf 00498 memset(errbuf, 0, sizeof(errbuf) ); 00499 00500 // main thread loop 00501 while (1) 00502 { 00503 pthread_t threadId; // Pthread variable that keeps the thread structures 00504 struct daemon_slpars *pars; // parameters needed by the daemon_serviceloop() 00505 00506 // Connection creation 00507 fromlen = sizeof(struct sockaddr_storage); 00508 00509 sockctrl= accept(sockmain, (struct sockaddr *) &from, &fromlen); 00510 00511 if (sockctrl == -1) 00512 { 00513 // The accept() call can return this error when a signal is catched 00514 // In this case, we have simply to ignore this error code 00515 // Stevens, pg 124 00516 #ifdef WIN32 00517 if (WSAGetLastError() == WSAEINTR) 00518 #else 00519 if (errno == EINTR) 00520 #endif 00521 continue; 00522 00523 // Don't check for errors here, since the error can be due to the fact that the thread 00524 // has been killed 00525 sock_geterror("accept(): ", errbuf, PCAP_ERRBUF_SIZE); 00526 SOCK_ASSERT(errbuf, 1); 00527 continue; 00528 } 00529 00530 // checks if the connecting host is among the ones allowed 00531 if (sock_check_hostlist(hostlist, RPCAP_HOSTLIST_SEP, &from, errbuf, PCAP_ERRBUF_SIZE) < 0 ) 00532 { 00533 rpcap_senderror(sockctrl, errbuf, PCAP_ERR_HOSTNOAUTH, fakeerrbuf); 00534 sock_close(sockctrl, fakeerrbuf, PCAP_ERRBUF_SIZE); 00535 continue; 00536 } 00537 00538 00539 #ifdef WIN32 00540 // in case of passive mode, this variable is deallocated by the daemon_serviceloop() 00541 pars= (struct daemon_slpars *) malloc ( sizeof(struct daemon_slpars) ); 00542 if (pars == NULL) 00543 { 00544 snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); 00545 continue; 00546 } 00547 00548 pars->sockctrl= sockctrl; 00549 pars->activeclose= 0; // useless in passive mode 00550 pars->isactive= 0; 00551 pars->nullAuthAllowed= nullAuthAllowed; 00552 00553 if ( pthread_create( &threadId, NULL, (void *) &daemon_serviceloop, (void *) pars) ) 00554 { 00555 SOCK_ASSERT("Error creating the child thread", 1); 00556 continue; 00557 } 00558 00559 #else 00560 if ( (pid= fork() ) == 0) // I am the child 00561 { 00562 // in case of passive mode, this variable is deallocated by the daemon_serviceloop() 00563 pars= (struct daemon_slpars *) malloc ( sizeof(struct daemon_slpars) ); 00564 if (pars == NULL) 00565 { 00566 snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); 00567 exit(0); 00568 } 00569 00570 pars->sockctrl= sockctrl; 00571 pars->activeclose= 0; // useless in passive mode 00572 pars->isactive= 0; 00573 pars->nullAuthAllowed= nullAuthAllowed; 00574 00575 // Close the main socket (must be open only in the parent) 00576 closesocket(sockmain); 00577 00578 daemon_serviceloop( (void *) pars); 00579 exit(0); 00580 } 00581 00582 // I am the parent 00583 // Close the childsocket (must be open only in the child) 00584 closesocket(sockctrl); 00585 #endif 00586 00587 // loop forever, until interrupted 00588 } 00589 } 00590 00591 00592 00593 00604 void main_active(void *ptr) 00605 { 00606 char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed 00607 SOCKET sockctrl; // keeps the socket ID for this control connection 00608 struct addrinfo hints; // temporary struct to keep settings needed to open the new socket 00609 struct addrinfo *addrinfo; // keeps the addrinfo chain; required to open a new socket 00610 struct active_pars *activepars; 00611 struct daemon_slpars *pars; // parameters needed by the daemon_serviceloop() 00612 00613 00614 activepars= (struct active_pars *) ptr; 00615 00616 // Prepare to open a new server socket 00617 memset(&hints, 0, sizeof(struct addrinfo)); 00618 // WARNING Currently it supports only ONE socket family among IPv4 and IPv6 00619 hints.ai_family = AF_INET; // PF_UNSPEC to have both IPv4 and IPv6 server 00620 hints.ai_socktype = SOCK_STREAM; 00621 hints.ai_family= activepars->ai_family; 00622 00623 snprintf(errbuf, PCAP_ERRBUF_SIZE, "Connecting to host %s, port %s, using protocol %s", 00624 activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4": 00625 (hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified"); 00626 SOCK_ASSERT(errbuf, 1); 00627 00628 // Initialize errbuf 00629 memset(errbuf, 0, sizeof(errbuf) ); 00630 00631 // Do the work 00632 if (sock_initaddress(activepars->address, activepars->port, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1) 00633 { 00634 SOCK_ASSERT(errbuf, 1); 00635 return; 00636 } 00637 00638 while (1) 00639 { 00640 int activeclose; 00641 00642 if ( (sockctrl= sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == -1) 00643 { 00644 SOCK_ASSERT(errbuf, 1); 00645 00646 snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error connecting to host %s, port %s, using protocol %s", 00647 activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4": 00648 (hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified" ); 00649 00650 SOCK_ASSERT(errbuf, 1); 00651 00652 pthread_suspend(RPCAP_ACTIVE_WAIT * 1000); 00653 00654 continue; 00655 } 00656 00657 pars= (struct daemon_slpars *) malloc ( sizeof(struct daemon_slpars) ); 00658 if (pars == NULL) 00659 { 00660 snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); 00661 continue; 00662 } 00663 00664 pars->sockctrl= sockctrl; 00665 pars->activeclose= 0; 00666 pars->isactive= 1; 00667 pars->nullAuthAllowed= nullAuthAllowed; 00668 00669 daemon_serviceloop( (void *) pars); 00670 00671 activeclose= pars->activeclose; 00672 00673 free(pars); 00674 00675 // If the connection is closed by the user explicitely, don't try to connect to it again 00676 // just exit the program 00677 if (activeclose == 1) 00678 break; 00679 } 00680 } 00681
documentation. Copyright (c) 2002-2003 Politecnico di Torino. All rights reserved.