int fileconf_save(const char *savefile) { FILE *fp; if ((fp = fopen(savefile, "w")) != NULL) { char *token; /*, *port;*/ // temp, needed to separate items into the hostlist char temphostlist[MAX_HOST_LIST + 1]; int i = 0; char *lasts; fprintf(fp, "# Configuration file help.\n\n"); // Save list of clients which are allowed to connect to us in passive mode fprintf(fp, "# Hosts which are allowed to connect to this server (passive mode)\n"); fprintf(fp, "# Format: PassiveClient = <name or address>\n\n"); strncpy(temphostlist, hostlist, MAX_HOST_LIST); temphostlist[MAX_HOST_LIST] = 0; token = pcap_strtok_r(temphostlist, RPCAP_HOSTLIST_SEP, &lasts); while(token != NULL) { fprintf(fp, "PassiveClient = %s\n", token); token = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts); } // Save list of clients which are allowed to connect to us in active mode fprintf(fp, "\n\n"); fprintf(fp, "# Hosts to which this server is trying to connect to (active mode)\n"); fprintf(fp, "# Format: ActiveClient = <name or address>, <port | DEFAULT>\n\n"); while ((i < MAX_ACTIVE_LIST) && (activelist[i].address[0] != 0)) { fprintf(fp, "ActiveClient = %s, %s\n", activelist[i].address, activelist[i].port); i++; } // Save if we want to permit NULL authentication fprintf(fp, "\n\n"); fprintf(fp, "# Permit NULL authentication: YES or NOT\n\n"); if (nullAuthAllowed) fprintf(fp, "NullAuthPermit = YES\n"); else fprintf(fp, "NullAuthPermit = NO\n"); fclose(fp); return 0; } else { return -1; } }
void fileconf_read(int sign) { FILE *fp; char msg[PCAP_ERRBUF_SIZE + 1]; int i; #ifndef _WIN32 signal(SIGHUP, fileconf_read); #endif if ((fp = fopen(loadfile, "r")) != NULL) { char line[MAX_LINE + 1]; char *ptr; hostlist[0] = 0; i = 0; while (fgets(line, MAX_LINE, fp) != NULL) { if (line[0] == '\n') continue; // Blank line if (line[0] == '\r') continue; // Blank line if (line[0] == '#') continue; // Comment ptr = strstr(line, "ActiveClient"); if (ptr) { char *address, *port; char *lasts; ptr = strchr(ptr, '=') + 1; address = pcap_strtok_r(ptr, RPCAP_HOSTLIST_SEP, &lasts); if ((address != NULL) && (i < MAX_ACTIVE_LIST)) { port = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts); strlcpy(activelist[i].address, address, MAX_LINE); if (strcmp(port, "DEFAULT") == 0) // the user choose a custom port strlcpy(activelist[i].port, RPCAP_DEFAULT_NETPORT_ACTIVE, MAX_LINE); else strlcpy(activelist[i].port, port, MAX_LINE); activelist[i].address[MAX_LINE] = 0; activelist[i].port[MAX_LINE] = 0; } else SOCK_ASSERT("Only MAX_ACTIVE_LIST active connections are currently supported.", 1); i++; continue; } ptr = strstr(line, "PassiveClient"); if (ptr) { ptr = strchr(ptr, '=') + 1; strlcat(hostlist, ptr, MAX_HOST_LIST); strlcat(hostlist, ",", MAX_HOST_LIST); continue; } ptr = strstr(line, "NullAuthPermit"); if (ptr) { ptr = strstr(ptr, "YES"); if (ptr) nullAuthAllowed = 1; else nullAuthAllowed = 0; continue; } } // clear the remaining fields of the active list while (i < MAX_ACTIVE_LIST) { activelist[i].address[0] = 0; activelist[i].port[0] = 0; i++; } // Remove all '\n' and '\r' from the strings strrem(hostlist, '\r'); strrem(hostlist, '\n'); pcap_snprintf(msg, PCAP_ERRBUF_SIZE, "New passive host list: %s\n\n", hostlist); SOCK_ASSERT(msg, 1); fclose(fp); } }
/* * \brief Checks that one host (identified by the sockaddr_storage structure) belongs to an 'allowed list'. * * This function is useful after an accept() call in order to check if the connecting * host is allowed to connect to me. To do that, we have a buffer that keeps the list of the * allowed host; this function checks the sockaddr_storage structure of the connecting host * against this host list, and it returns '0' is the host is included in this list. * * \param hostlist: pointer to a string that contains the list of the allowed host. * * \param sep: a string that keeps the separators used between the hosts (for example the * space character) in the host list. * * \param from: a sockaddr_storage structure, as it is returned by the accept() call. * * \param errbuf: a pointer to an user-allocated buffer that will contain the complete * error message. This buffer has to be at least 'errbuflen' in length. * It can be NULL; in this case the error cannot be printed. * * \param errbuflen: length of the buffer that will contains the error. The error message cannot be * larger than 'errbuflen - 1' because the last char is reserved for the string terminator. * * \return It returns: * - '1' if the host list is empty * - '0' if the host belongs to the host list (and therefore it is allowed to connect) * - '-1' in case the host does not belong to the host list (and therefore it is not allowed to connect * - '-2' in case or error. The error message is returned in the 'errbuf' variable. */ int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage *from, char *errbuf, int errbuflen) { /* checks if the connecting host is among the ones allowed */ if ((hostlist) && (hostlist[0])) { char *token; /* temp, needed to separate items into the hostlist */ struct addrinfo *addrinfo, *ai_next; char *temphostlist; char *lasts; /* * The problem is that strtok modifies the original variable by putting '0' at the end of each token * So, we have to create a new temporary string in which the original content is kept */ temphostlist = strdup(hostlist); if (temphostlist == NULL) { sock_geterror("sock_check_hostlist(), malloc() failed", errbuf, errbuflen); return -2; } token = pcap_strtok_r(temphostlist, sep, &lasts); /* it avoids a warning in the compilation ('addrinfo used but not initialized') */ addrinfo = NULL; while (token != NULL) { struct addrinfo hints; int retval; addrinfo = NULL; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; retval = getaddrinfo(token, "0", &hints, &addrinfo); if (retval != 0) { if (errbuf) pcap_snprintf(errbuf, errbuflen, "getaddrinfo() %s", gai_strerror(retval)); SOCK_ASSERT(errbuf, 1); /* Get next token */ token = pcap_strtok_r(NULL, sep, &lasts); continue; } /* ai_next is required to preserve the content of addrinfo, in order to deallocate it properly */ ai_next = addrinfo; while (ai_next) { if (sock_cmpaddr(from, (struct sockaddr_storage *) ai_next->ai_addr) == 0) { free(temphostlist); freeaddrinfo(addrinfo); return 0; } /* * If we are here, it means that the current address does not matches * Let's try with the next one in the header chain */ ai_next = ai_next->ai_next; } freeaddrinfo(addrinfo); addrinfo = NULL; /* Get next token */ token = pcap_strtok_r(NULL, sep, &lasts); } if (addrinfo) { freeaddrinfo(addrinfo); addrinfo = NULL; } if (errbuf) pcap_snprintf(errbuf, errbuflen, "The host is not in the allowed host list. Connection refused."); free(temphostlist); return -1; } /* No hostlist, so we have to return 'empty list' */ return 1; }
//! Program main int main(int argc, char *argv[], char *envp[]) { char savefile[MAX_LINE + 1]; // name of the file on which we have to save the configuration int isdaemon = 0; // Not null if the user wants to run this program as a daemon int retval; // keeps the returning value from several functions char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed savefile[0] = 0; loadfile[0] = 0; hostlist[0] = 0; // Initialize errbuf memset(errbuf, 0, sizeof(errbuf)); if (sock_init(errbuf, PCAP_ERRBUF_SIZE) == -1) { SOCK_ASSERT(errbuf, 1); exit(-1); } strncpy(address, RPCAP_DEFAULT_NETADDR, MAX_LINE); strncpy(port, RPCAP_DEFAULT_NETPORT, MAX_LINE); // Prepare to open a new server socket memset(&mainhints, 0, sizeof(struct addrinfo)); mainhints.ai_family = PF_UNSPEC; mainhints.ai_flags = AI_PASSIVE; // Ready to a bind() socket mainhints.ai_socktype = SOCK_STREAM; // Getting the proper command line options while ((retval = getopt(argc, argv, "b:dhp:4l:na:s:f:v")) != -1) { switch (retval) { case 'b': strncpy(address, optarg, MAX_LINE); break; case 'p': strncpy(port, optarg, MAX_LINE); break; case '4': mainhints.ai_family = PF_INET; // IPv4 server only break; case 'd': isdaemon = 1; break; case 'n': nullAuthAllowed = 1; break; case 'v': passivemode = 0; break; case 'l': { strncpy(hostlist, optarg, sizeof(hostlist)); break; } case 'a': { char *tmpaddress, *tmpport; char *lasts; int i = 0; tmpaddress = pcap_strtok_r(optarg, RPCAP_HOSTLIST_SEP, &lasts); while ((tmpaddress != NULL) && (i < MAX_ACTIVE_LIST)) { tmpport = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts); strlcpy(activelist[i].address, tmpaddress, MAX_LINE); if ((tmpport == NULL) || (strcmp(tmpport, "DEFAULT") == 0)) // the user choose a custom port strlcpy(activelist[i].port, RPCAP_DEFAULT_NETPORT_ACTIVE, MAX_LINE); else strlcpy(activelist[i].port, tmpport, MAX_LINE); tmpaddress = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts); i++; } if (i > MAX_ACTIVE_LIST) SOCK_ASSERT("Only MAX_ACTIVE_LIST active connections are currently supported.", 1); // I don't initialize the remaining part of the structure, since // it is already zeroed (it is a global var) break; } case 'f': strlcpy(loadfile, optarg, MAX_LINE); break; case 's': strlcpy(savefile, optarg, MAX_LINE); break; case 'h': printusage(); exit(0); default: break; } } if (savefile[0]) { if (fileconf_save(savefile)) SOCK_ASSERT("Error when saving the configuration to file", 1); } // If the file does not exist, it keeps the settings provided by the command line if (loadfile[0]) fileconf_read(0); #ifndef _WIN32 // SIGTERM (i.e. kill -15) is not generated in Win32, although it is included for ANSI compatibility signal(SIGTERM, main_cleanup); signal(SIGCHLD, main_cleanup_childs); #endif // forking a daemon, if it is needed if (isdaemon) { #ifndef _WIN32 int pid; // Unix Network Programming, pg 336 if ((pid = fork()) != 0) exit(0); // Parent terminates // First child continues // Set daemon mode setsid(); // generated under unix with 'kill -HUP', needed to reload the configuration signal(SIGHUP, fileconf_read); if ((pid = fork()) != 0) exit(0); // First child terminates // LINUX WARNING: the current linux implementation of pthreads requires a management thread // to handle some hidden stuff. So, as soon as you create the first thread, two threads are // created. Fom this point on, the number of threads active are always one more compared // to the number you're expecting // Second child continues // umask(0); // chdir("/"); #else // We use the SIGABRT signal to kill the Win32 service signal(SIGABRT, main_cleanup); // If this call succeeds, it is blocking on Win32 if (svc_start() != 1) SOCK_ASSERT("Unable to start the service", 1); // When the previous call returns, the entire application has to be stopped. exit(0); #endif } else // Console mode { // Enable the catching of Ctrl+C signal(SIGINT, main_cleanup); #ifndef _WIN32 // generated under unix with 'kill -HUP', needed to reload the configuration // We do not have this kind of signal in Win32 signal(SIGHUP, fileconf_read); #endif printf("Press CTRL + C to stop the server...\n"); } // If we're a Win32 service, we have already called this function in the service_main main_startup(); // The code should never arrive here (since the main_startup is blocking) // however this avoids a compiler warning exit(0); }