void readConfiguration(void) { FILE *in; char line[16384]; int lnum = 0; int i; int ai; int di; if (seFds) { /* Close existing server sockets. */ for (i = 0; (i < seTotal); i++) { if (seFds[i] != -1) { closesocket(seFds[i]); free(seFromHosts[i]); free(seToHosts[i]); } } /* Free memory associated with previous set. */ free(seFds); free(seLocalAddrs); free(seLocalPorts); free(seFromHosts); free(seFromPorts); free(seToHosts); free(seToPorts); free(seAllowRules); free(seDenyRules); free(seAllowRulesTotal); free(seDenyRulesTotal); } seTotal = 0; if (allowRules) { /* Forget existing allow rules. */ for (i = 0; (i < allowRulesTotal); i++) { free(allowRules[i]); } /* Free memory associated with previous set. */ free(allowRules); globalAllowRules = 0; } allowRulesTotal = 0; if (denyRules) { /* Forget existing deny rules. */ for (i = 0; (i < denyRulesTotal); i++) { free(denyRules[i]); } /* Free memory associated with previous set. */ free(denyRules); globalDenyRules = 0; } denyRulesTotal = 0; if (logFileName) { free(logFileName); logFileName = 0; } if (pidLogFileName) { free(pidLogFileName); pidLogFileName = 0; } /* 1. Count the non-comment lines of each type and allocate space for the data. */ in = fopen(options.conf_file, "r"); if (!in) { fprintf(stderr, "rinetd: can't open %s\n", options.conf_file); exit(1); } while (1) { char *t = 0; if (!getConfLine(in, line, sizeof(line), &lnum)) { break; } t = strtok(line, " \t\r\n"); if (!strcmp(t, "logfile")) { continue; } else if (!strcmp(t, "pidlogfile")) { continue; } else if (!strcmp(t, "logcommon")) { continue; } else if (!strcmp(t, "allow")) { allowRulesTotal++; } else if (!strcmp(t, "deny")) { denyRulesTotal++; } else { /* A regular forwarding rule */ seTotal++; } } fclose(in); seFds = (SOCKET *) malloc(sizeof(int) * seTotal); if (!seFds) { goto lowMemory; } seLocalAddrs = (struct in_addr *) malloc(sizeof(struct in_addr) * seTotal); if (!seLocalAddrs) { goto lowMemory; } seLocalPorts = (unsigned short *) malloc(sizeof(unsigned short) * seTotal); if (!seLocalPorts) { goto lowMemory; } seFromHosts = (char **) malloc(sizeof(char *) * seTotal); if (!seFromHosts) { goto lowMemory; } seFromPorts = (int *) malloc(sizeof(int) * seTotal); if (!seFromPorts) { goto lowMemory; } seToHosts = (char **) malloc(sizeof(char *) * seTotal); if (!seToHosts) { goto lowMemory; } seToPorts = (int *) malloc(sizeof(int) * seTotal); if (!seToPorts) { goto lowMemory; } allowRules = (char **) malloc(sizeof(char *) * allowRulesTotal); if (!allowRules) { goto lowMemory; } denyRules = (char **) malloc(sizeof(char *) * denyRulesTotal); if (!denyRules) { goto lowMemory; } seAllowRules = (int *) malloc(sizeof(int) * seTotal); if (!seAllowRules) { goto lowMemory; } seAllowRulesTotal = (int *) malloc(sizeof(int) * seTotal); if (!seAllowRulesTotal) { goto lowMemory; } seDenyRules = (int *) malloc(sizeof(int) * seTotal); if (!seDenyRules) { goto lowMemory; } seDenyRulesTotal = (int *) malloc(sizeof(int) * seTotal); if (!seDenyRulesTotal) { goto lowMemory; } /* 2. Make a second pass to configure them. */ i = 0; ai = 0; di = 0; lnum = 0; in = fopen(options.conf_file, "r"); if (!in) { goto lowMemory; } if (seTotal > 0) { seAllowRulesTotal[i] = 0; seDenyRulesTotal[i] = 0; } while (1) { char *bindAddress; unsigned short bindPort; char *connectAddress; char *bindPortS; char *connectPortS; unsigned short connectPort; struct in_addr iaddr; struct sockaddr_in saddr; struct servent *service; int j; if (!getConfLine(in, line, sizeof(line), &lnum)) { break; } bindAddress = strtok(line, " \t\r\n"); if (!bindAddress) { fprintf(stderr, "rinetd: no bind address specified " "on line %d.\n", lnum); continue; } if (!strcmp(bindAddress, "allow")) { char *pattern = strtok(0, " \t\r\n"); if (!pattern) { fprintf(stderr, "rinetd: nothing to allow " "specified on line %d.\n", lnum); continue; } if (patternBad(pattern)) { fprintf(stderr, "rinetd: illegal allow or " "deny pattern. Only digits, ., and\n" "the ? and * wild cards are allowed. " "For performance reasons, rinetd\n" "does not look up complete " "host names.\n"); continue; } allowRules[ai] = malloc(strlen(pattern) + 1); if (!allowRules[ai]) { goto lowMemory; } strcpy(allowRules[ai], pattern); if (i > 0) { if (seAllowRulesTotal[i - 1] == 0) { seAllowRules[i - 1] = ai; } seAllowRulesTotal[i - 1]++; } else { globalAllowRules++; } ai++; } else if (!strcmp(bindAddress, "deny")) { char *pattern = strtok(0, " \t\r\n"); if (!pattern) { fprintf(stderr, "rinetd: nothing to deny " "specified on line %d.\n", lnum); continue; } denyRules[di] = malloc(strlen(pattern) + 1); if (!denyRules[di]) { goto lowMemory; } strcpy(denyRules[di], pattern); if (i > 0) { if (seDenyRulesTotal[i - 1] == 0) { seDenyRules[i - 1] = di; } seDenyRulesTotal[i - 1]++; } else { globalDenyRules++; } di++; } else if (!strcmp(bindAddress, "logfile")) { char *nt = strtok(0, " \t\r\n"); if (!nt) { fprintf(stderr, "rinetd: no log file name " "specified on line %d.\n", lnum); continue; } logFileName = malloc(strlen(nt) + 1); if (!logFileName) { goto lowMemory; } strcpy(logFileName, nt); } else if (!strcmp(bindAddress, "pidlogfile")) { char *nt = strtok(0, " \t\r\n"); if (!nt) { fprintf(stderr, "rinetd: no PID log file name " "specified on line %d.\n", lnum); continue; } pidLogFileName = malloc(strlen(nt) + 1); if (!pidLogFileName) { goto lowMemory; } strcpy(pidLogFileName, nt); } else if (!strcmp(bindAddress, "logcommon")) { logFormatCommon = 1; } else { /* A regular forwarding rule. */ bindPortS = strtok(0, " \t\r\n"); if (!bindPortS) { fprintf(stderr, "rinetd: no bind port " "specified on line %d.\n", lnum); continue; } service = getservbyname(bindPortS, "tcp"); if (service) { bindPort = ntohs(service->s_port); } else { bindPort = atoi(bindPortS); } if ((bindPort == 0) || (bindPort >= 65536)) { fprintf(stderr, "rinetd: bind port missing " "or out of range on line %d.\n", lnum); continue; } connectAddress = strtok(0, " \t\r\n"); if (!connectAddress) { fprintf(stderr, "rinetd: no connect address " "specified on line %d.\n", lnum); continue; } connectPortS = strtok(0, " \t\r\n"); if (!connectPortS) { fprintf(stderr, "rinetd: no connect port " "specified on line %d.\n", lnum); continue; } service = getservbyname(connectPortS, "tcp"); if (service) { connectPort = ntohs(service->s_port); } else { connectPort = atoi(connectPortS); } if ((connectPort == 0) || (connectPort >= 65536)) { fprintf(stderr, "rinetd: bind port missing " "or out of range on line %d.\n", lnum); continue; } /* Turn all of this stuff into reasonable addresses */ if (!getAddress(bindAddress, &iaddr)) { fprintf(stderr, "rinetd: host %s could not be " "resolved on line %d.\n", bindAddress, lnum); continue; } /* Make a server socket */ seFds[i] = socket(PF_INET, SOCK_STREAM, 0); if (seFds[i] == INVALID_SOCKET) { fprintf(stderr, "rinetd: couldn't create " "server socket!\n"); seFds[i] = -1; continue; } #ifndef WIN32 if (seFds[i] > maxfd) { maxfd = seFds[i]; } #endif saddr.sin_family = AF_INET; memcpy(&saddr.sin_addr, &iaddr, sizeof(iaddr)); saddr.sin_port = htons(bindPort); j = 1; setsockopt(seFds[i], SOL_SOCKET, SO_REUSEADDR, (const char *) &j, sizeof(j)); if (bind(seFds[i], (struct sockaddr *) &saddr, sizeof(saddr)) == SOCKET_ERROR) { /* Warn -- don't exit. */ fprintf(stderr, "rinetd: couldn't bind to " "address %s port %d\n", bindAddress, bindPort); closesocket(seFds[i]); seFds[i] = INVALID_SOCKET; continue; } if (listen(seFds[i], 5) == SOCKET_ERROR) { /* Warn -- don't exit. */ fprintf(stderr, "rinetd: couldn't listen to " "address %s port %d\n", bindAddress, bindPort); closesocket(seFds[i]); seFds[i] = INVALID_SOCKET; continue; } ioctlsocket(seFds[i], FIONBIO, &j); if (!getAddress(connectAddress, &iaddr)) { /* Warn -- don't exit. */ fprintf(stderr, "rinetd: host %s could not be " "resolved on line %d.\n", bindAddress, lnum); closesocket(seFds[i]); seFds[i] = INVALID_SOCKET; continue; } seLocalAddrs[i] = iaddr; seLocalPorts[i] = htons(connectPort); seFromHosts[i] = malloc(strlen(bindAddress) + 1); if (!seFromHosts[i]) { goto lowMemory; } strcpy(seFromHosts[i], bindAddress); seFromPorts[i] = bindPort; seToHosts[i] = malloc(strlen(connectAddress) + 1); if (!seToHosts[i]) { goto lowMemory; } strcpy(seToHosts[i], connectAddress); seToPorts[i] = connectPort; i++; if (i < seTotal) { seAllowRulesTotal[i] = 0; seDenyRulesTotal[i] = 0; } } } /* Open the log file */ if (logFile) { fclose(logFile); logFile = 0; } if (logFileName) { logFile = fopen(logFileName, "a"); if (!logFile) { fprintf(stderr, "rinetd: could not open %s to append.\n", logFileName); } } return; lowMemory: fprintf(stderr, "rinetd: not enough memory to start rinetd.\n"); exit(1); }
static void readConfiguration(void) { /* Parse the configuration file. */ FILE *in = fopen(options.conf_file, "r"); if (!in) { goto lowMemory; } for (int lnum = 0; ; ) { char line[16384]; if (!getConfLine(in, line, sizeof(line), &lnum)) { break; } char const *currentToken = strtok(line, " \t\r\n"); if (!currentToken) { syslog(LOG_ERR, "no bind address specified " "on file %s, line %d.\n", options.conf_file, lnum); continue; } if (!strcmp(currentToken, "allow") || !strcmp(currentToken, "deny")) { char const *pattern = strtok(0, " \t\r\n"); if (!pattern) { syslog(LOG_ERR, "nothing to %s " "specified on file %s, line %d.\n", currentToken, options.conf_file, lnum); continue; } int bad = 0; for (char const *p = pattern; *p; ++p) { if (!strchr("0123456789?*.", *p)) { bad = 1; break; } } if (bad) { syslog(LOG_ERR, "illegal allow or " "deny pattern. Only digits, ., and\n" "the ? and * wild cards are allowed. " "For performance reasons, rinetd\n" "does not look up complete " "host names.\n"); continue; } allRules = (Rule *) realloc(allRules, sizeof(Rule *) * (allRulesCount + 1)); if (!allRules) { goto lowMemory; } allRules[allRulesCount].pattern = strdup(pattern); if (!allRules[allRulesCount].pattern) { goto lowMemory; } allRules[allRulesCount].type = currentToken[0] == 'a' ? allowRule : denyRule; if (seTotal > 0) { if (seInfo[seTotal - 1].rulesStart == 0) { seInfo[seTotal - 1].rulesStart = allRulesCount; } ++seInfo[seTotal - 1].rulesCount; } else { ++globalRulesCount; } ++allRulesCount; } else if (!strcmp(currentToken, "logfile")) { char const *nt = strtok(0, " \t\r\n"); if (!nt) { syslog(LOG_ERR, "no log file name " "specified on file %s, line %d.\n", options.conf_file, lnum); continue; } logFileName = strdup(nt); if (!logFileName) { goto lowMemory; } } else if (!strcmp(currentToken, "pidlogfile")) { char const *nt = strtok(0, " \t\r\n"); if (!nt) { syslog(LOG_ERR, "no PID log file name " "specified on file %s, line %d.\n", options.conf_file, lnum); continue; } pidLogFileName = strdup(nt); if (!pidLogFileName) { goto lowMemory; } } else if (!strcmp(currentToken, "logcommon")) { logFormatCommon = 1; } else { /* A regular forwarding rule. */ char const *bindAddress = currentToken; char const *bindPortS = strtok(0, " \t\r\n"); if (!bindPortS) { syslog(LOG_ERR, "no bind port " "specified on file %s, line %d.\n", options.conf_file, lnum); continue; } struct servent *bindService = getservbyname(bindPortS, "tcp"); unsigned int bindPort = bindService ? ntohs(bindService->s_port) : atoi(bindPortS); if (bindPort == 0 || bindPort >= 65536) { syslog(LOG_ERR, "bind port missing " "or out of range on file %s, line %d.\n", options.conf_file, lnum); continue; } char const *connectAddress = strtok(0, " \t\r\n"); if (!connectAddress) { syslog(LOG_ERR, "no connect address " "specified on file %s, line %d.\n", options.conf_file, lnum); continue; } char const *connectPortS = strtok(0, " \t\r\n"); if (!connectPortS) { syslog(LOG_ERR, "no connect port " "specified on file %s, line %d.\n", options.conf_file, lnum); continue; } struct servent *connectService = getservbyname(connectPortS, "tcp"); unsigned int connectPort = connectService ? ntohs(connectService->s_port) : atoi(connectPortS); if (connectPort == 0 || connectPort >= 65536) { syslog(LOG_ERR, "bind port missing " "or out of range on file %s, %d.\n", options.conf_file, lnum); continue; } /* Turn all of this stuff into reasonable addresses */ struct in_addr iaddr; if (getAddress(bindAddress, &iaddr) < 0) { fprintf(stderr, "rinetd: host %s could not be " "resolved on line %d.\n", bindAddress, lnum); continue; } /* Make a server socket */ SOCKET fd = socket(PF_INET, SOCK_STREAM, 0); if (fd == INVALID_SOCKET) { syslog(LOG_ERR, "couldn't create " "server socket! (%m)\n"); continue; } struct sockaddr_in saddr; saddr.sin_family = AF_INET; memcpy(&saddr.sin_addr, &iaddr, sizeof(iaddr)); saddr.sin_port = htons(bindPort); int tmp = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *) &tmp, sizeof(tmp)); if (bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) == SOCKET_ERROR) { /* Warn -- don't exit. */ syslog(LOG_ERR, "couldn't bind to " "address %s port %d (%m)\n", bindAddress, bindPort); closesocket(fd); continue; } if (listen(fd, RINETD_LISTEN_BACKLOG) == SOCKET_ERROR) { /* Warn -- don't exit. */ syslog(LOG_ERR, "couldn't listen to " "address %s port %d (%m)\n", bindAddress, bindPort); closesocket(fd); continue; } #if _WIN32 u_long ioctltmp; #else int ioctltmp; #endif ioctlsocket(fd, FIONBIO, &ioctltmp); if (getAddress(connectAddress, &iaddr) < 0) { /* Warn -- don't exit. */ syslog(LOG_ERR, "host %s could not be " "resolved on file %s, line %d.\n", bindAddress, options.conf_file, lnum); closesocket(fd); continue; } /* Allocate server info */ seInfo = (ServerInfo *) realloc(seInfo, sizeof(ServerInfo) * (seTotal + 1)); if (!seInfo) { goto lowMemory; } ServerInfo *srv = &seInfo[seTotal]; memset(srv, 0, sizeof(*srv)); srv->fd = fd; srv->localAddr = iaddr; srv->localPort = htons(connectPort); srv->fromHost = strdup(bindAddress); if (!srv->fromHost) { goto lowMemory; } srv->fromPort = bindPort; srv->toHost = strdup(connectAddress); if (!srv->toHost) { goto lowMemory; } srv->toPort = connectPort; #ifndef _WIN32 if (fd > maxfd) { maxfd = fd; } #endif ++seTotal; } } fclose(in); /* Open the log file */ if (logFile) { fclose(logFile); logFile = NULL; } if (logFileName) { logFile = fopen(logFileName, "a"); if (logFile) { setvbuf(logFile, NULL, _IONBF, 0); } else { syslog(LOG_ERR, "could not open %s to append (%m).\n", logFileName); } } return; lowMemory: syslog(LOG_ERR, "not enough memory to start rinetd.\n"); exit(1); }