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);
}
Пример #2
0
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);
}