Skip to content
Snippets Groups Projects
ftpsrvr.c 160 KiB
Newer Older
	socket_debug[sock]&=~SOCKET_DEBUG_TERMINATE;
#endif
rswindell's avatar
rswindell committed

deuce's avatar
deuce committed
	ftp_close_socket(&tmp_sock,&sess,__LINE__);
		int32_t	clients = protected_uint32_adjust_fetch(&active_clients, -1);
		int32_t	threads = thread_down();
		lprintf(LOG_INFO,"%04d CTRL thread terminated (%d clients and %d threads remain, %lu served)"
			,sock, clients, threads, served);
rswindell's avatar
rswindell committed
static void cleanup(int code, int line)
rswindell's avatar
rswindell committed
#ifdef _DEBUG
	lprintf(LOG_DEBUG,"0000 cleanup called from line %d",line);
rswindell's avatar
rswindell committed
#endif
	if(protected_uint32_value(thread_count) > 1) {
		lprintf(LOG_INFO, "0000 Waiting for %d child threads to terminate", protected_uint32_value(thread_count)-1);
		while(protected_uint32_value(thread_count) > 1) {
			mswait(100);
		}
		lprintf(LOG_INFO, "0000 Done waiting for child threads to terminate");
	free_cfg(&scfg);
	free_text(text);

	semfile_list_free(&recycle_semfiles);
	semfile_list_free(&shutdown_semfiles);
deuce's avatar
deuce committed
	if(ftp_set != NULL) {
		xpms_destroy(ftp_set, ftp_close_socket_cb, NULL);
		ftp_set = NULL;
	}
	update_clients();	/* active_clients is destroyed below */
	listFree(&current_connections);

	if(protected_uint32_value(active_clients))
		lprintf(LOG_WARNING,"!!!! Terminating with %d active clients", protected_uint32_value(active_clients));
		protected_uint32_destroy(active_clients);
	if(WSAInitialized && WSACleanup()!=0) 
		lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
rswindell's avatar
rswindell committed
	thread_down();
	status("Down");
	if(terminate_server || code)
		lprintf(LOG_INFO,"#### FTP Server thread terminated (%lu clients served)", served);
	if(startup!=NULL && startup->terminated!=NULL)
		startup->terminated(startup->cbdata,code);
const char* DLLCALL ftp_ver(void)
{
	static char ver[256];
	char compiler[32];

	DESCRIBE_COMPILER(compiler);
	safe_snprintf(ver, sizeof(ver), "%s %s%c%s  "
		"Compiled %s/%s %s %s with %s"
		,FTP_SERVER
#ifdef _DEBUG
		," Debug"
#else
		,""
#endif
		,__DATE__, __TIME__, compiler);

	return(ver);
}

	char			compiler[32];
deuce's avatar
deuce committed
	union xp_sockaddr client_addr;
	socklen_t		client_addr_len;
	SOCKET			client_socket;
	int				i;
	time_t			t;
	time_t			start;
deuce's avatar
deuce committed
	char			client_ip[INET6_ADDRSTRLEN];
deuce's avatar
deuce committed
	CRYPT_SESSION		none = -1;

	startup=(ftp_startup_t*)arg;
	SetThreadName("sbbs/ftpServer");
	if(thread_suid_broken)
		startup->seteuid(TRUE);
    if(startup==NULL) {
    	fprintf(stderr, "No startup structure passed!\n");
    	return;
    }

	if(startup->size!=sizeof(ftp_startup_t)) {	/* verify size */
		sbbs_beep(100,500);
		sbbs_beep(300,500);
		sbbs_beep(100,500);
		fprintf(stderr, "Invalid startup structure!\n");
		return;
	}

	startup->recycle_now=FALSE;
	protected_uint32_init(&thread_count, 0);
		listInit(&current_connections, LINK_LIST_MUTEX);
		protected_uint32_init(&active_clients, 0);
		/* Setup intelligent defaults */
		if(startup->port==0)					startup->port=IPPORT_FTP;
		if(startup->qwk_timeout==0)				startup->qwk_timeout=FTP_DEFAULT_QWK_TIMEOUT;		/* seconds */
		if(startup->max_inactivity==0)			startup->max_inactivity=FTP_DEFAULT_MAX_INACTIVITY;	/* seconds */
		if(startup->sem_chk_freq==0)			startup->sem_chk_freq=DEFAULT_SEM_CHK_FREQ;		/* seconds */
		if(startup->index_file_name[0]==0)		SAFECOPY(startup->index_file_name,"00index");
		(void)protected_uint32_adjust(&thread_count,1);
		memset(&scfg, 0, sizeof(scfg));
		lprintf(LOG_INFO,"Synchronet FTP Server Version %s%c%s"
			,VERSION, REVISION
		DESCRIBE_COMPILER(compiler);
		lprintf(LOG_INFO,"Compiled %s/%s %s %s with %s", git_branch, git_hash, __DATE__, __TIME__, compiler);
		sbbs_srand();	/* Seed random number generator */
rswindell's avatar
rswindell committed
			cleanup(1,__LINE__);
		lprintf(LOG_INFO,"Initializing on %.24s with options: %x"
			,ctime_r(&t,str),startup->options);
rswindell's avatar
rswindell committed
			lprintf(LOG_ERR,"!ERROR %d (%s) changing directory to: %s", errno, strerror(errno), startup->ctrl_dir);
		/* Initial configuration and load from CNF files */
		SAFECOPY(scfg.ctrl_dir, startup->ctrl_dir);
		lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir);
		SAFECOPY(error,UNKNOWN_LOAD_ERROR);
		if(!load_cfg(&scfg, text, /* prep: */TRUE, /* node: */FALSE, error, sizeof(error))) {
			lprintf(LOG_CRIT,"!ERROR %s",error);
			lprintf(LOG_CRIT,"!Failed to load configuration files");
rswindell's avatar
rswindell committed
			cleanup(1,__LINE__);
		if((t=checktime())!=0) {   /* Check binary time */
			lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
			uptime=time(NULL);	/* this must be done *after* setting the timezone */
			SAFECOPY(scfg.temp_dir,startup->temp_dir);
			SAFECOPY(scfg.temp_dir,"../temp");
	   	prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
		if((i = md(scfg.temp_dir)) != 0) {
			lprintf(LOG_CRIT,"!ERROR %d (%s) creating directory: %s", i, strerror(i), scfg.temp_dir);
		lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
		if(!startup->max_clients) {
			startup->max_clients=scfg.sys_nodes;
			if(startup->max_clients<10)
				startup->max_clients=10;
		}
		lprintf(LOG_DEBUG,"Maximum clients: %d",startup->max_clients);
		/* Sanity-check the passive port range */
		if(startup->pasv_port_low || startup->pasv_port_high) {
			if(startup->pasv_port_low > startup->pasv_port_high
				|| startup->pasv_port_high-startup->pasv_port_low < (startup->max_clients-1)) {
				lprintf(LOG_WARNING,"!Correcting Passive Port Range (Low: %u, High: %u)"
					,startup->pasv_port_low,startup->pasv_port_high);
				if(startup->pasv_port_low)
					startup->pasv_port_high = startup->pasv_port_low+(startup->max_clients-1);
				else
					startup->pasv_port_low = startup->pasv_port_high-(startup->max_clients-1);
			}
			lprintf(LOG_DEBUG,"Passive Port Low: %u",startup->pasv_port_low);
			lprintf(LOG_DEBUG,"Passive Port High: %u",startup->pasv_port_high);
		}

		lprintf(LOG_DEBUG,"Maximum inactivity: %d seconds",startup->max_inactivity);
		strlwr(scfg.sys_id); /* Use lower-case unix-looking System ID for group name */
		for(i=0;i<scfg.total_libs;i++) {
			strlwr(scfg.lib[i]->sname);
			dotname(scfg.lib[i]->sname,scfg.lib[i]->sname);
		}
		/* open a socket and wait for a client */
deuce's avatar
deuce committed
		ftp_set = xpms_create(startup->bind_retry_count, startup->bind_retry_delay, lprintf);
		
		if(ftp_set == NULL) {
			lprintf(LOG_CRIT,"!ERROR %d creating FTP socket set", ERROR_VALUE);
			cleanup(1, __LINE__);
			return;
deuce's avatar
deuce committed
		lprintf(LOG_DEBUG,"FTP Server socket set created");
deuce's avatar
deuce committed
		/*
		 * Add interfaces
		 */
		xpms_add_list(ftp_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->port, "FTP Server", ftp_open_socket_cb, startup->seteuid, NULL);
		/* Setup recycle/shutdown semaphore file lists */
		shutdown_semfiles=semfile_list_init(scfg.ctrl_dir,"shutdown","ftp");
		recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","ftp");
		semfile_list_add(&recycle_semfiles,startup->ini_fname);
		SAFEPRINTF(path,"%sftpsrvr.rec",scfg.ctrl_dir);	/* legacy */
		semfile_list_add(&recycle_semfiles,path);
		if(!initialized) {
			semfile_list_check(&initialized,recycle_semfiles);
			semfile_list_check(&initialized,shutdown_semfiles);
		/* signal caller that we've started up successfully */
		if(startup->started!=NULL)
deuce's avatar
deuce committed
		lprintf(LOG_INFO,"FTP Server thread started");
deuce's avatar
deuce committed
		while(ftp_set!=NULL && !terminate_server) {
			if(protected_uint32_value(thread_count) <= 1) {
				if(!(startup->options&FTP_OPT_NO_RECYCLE)) {
					if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) {
						lprintf(LOG_INFO,"0000 Recycle semaphore file (%s) detected",p);
						break;
					}
					if(startup->recycle_now==TRUE) {
						lprintf(LOG_NOTICE,"0000 Recycle semaphore signaled");
						startup->recycle_now=FALSE;
						break;
					}
				if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL
						&& lprintf(LOG_INFO,"0000 Shutdown semaphore file (%s) detected",p))
					|| (startup->shutdown_now==TRUE
						&& lprintf(LOG_INFO,"0000 Shutdown semaphore signaled"))) {
					startup->shutdown_now=FALSE;
					terminate_server=TRUE;
deuce's avatar
deuce committed
			if(ftp_set==NULL || terminate_server)	/* terminated */
deuce's avatar
deuce committed
			/* now wait for connection */
			client_addr_len = sizeof(client_addr);
			client_socket = xpms_accept(ftp_set, &client_addr, &client_addr_len, startup->sem_chk_freq*1000, XPMS_FLAGS_NONE, NULL);
			if(client_socket == INVALID_SOCKET)
deuce's avatar
deuce committed

				startup->socket_open(startup->cbdata,TRUE);
deuce's avatar
deuce committed
			inet_addrtop(&client_addr, client_ip, sizeof(client_ip));

			if(startup->max_concurrent_connections > 0) {
				int ip_len = strlen(client_ip) + 1;
				uint connections = listCountMatches(&current_connections, client_ip, ip_len);
				if(connections >= startup->max_concurrent_connections
					&& !is_host_exempt(&scfg, client_ip, /* host_name */NULL)) {
					lprintf(LOG_NOTICE, "%04d [%s] !Maximum concurrent connections (%u) exceeded"
 						,client_socket, client_ip, startup->max_concurrent_connections);
					sockprintf(client_socket, -1, "421 Maximum connections (%u) exceeded", startup->max_concurrent_connections);
					ftp_close_socket(&client_socket,&none,__LINE__);
					continue;
				}
			}

deuce's avatar
deuce committed
			if(trashcan(&scfg,client_ip,"ip-silent")) {
deuce's avatar
deuce committed
				ftp_close_socket(&client_socket,&none,__LINE__);
			if(protected_uint32_value(active_clients)>=startup->max_clients) {
runderwo's avatar
 
runderwo committed
				lprintf(LOG_WARNING,"%04d !MAXIMUM CLIENTS (%d) reached, access denied"
					,client_socket, startup->max_clients);
deuce's avatar
deuce committed
				sockprintf(client_socket,-1,"421 Maximum active clients reached, please try again later.");
				ftp_close_socket(&client_socket,&none,__LINE__);
			if((ftp=malloc(sizeof(ftp_t)))==NULL) {
				lprintf(LOG_CRIT,"%04d !ERROR allocating %d bytes of memory for ftp_t"
					,client_socket,(int)sizeof(ftp_t));
deuce's avatar
deuce committed
				sockprintf(client_socket,-1,"421 System error, please try again later.");
				ftp_close_socket(&client_socket,&none,__LINE__);
deuce's avatar
deuce committed
			memcpy(&ftp->client_addr, &client_addr, client_addr_len);
			ftp->client_addr_len = client_addr_len;
			(void)protected_uint32_adjust(&thread_count,1);
#if 0 /* def _DEBUG */
		lprintf(LOG_DEBUG,"0000 terminate_server: %d",terminate_server);
#endif
		if(protected_uint32_value(active_clients)) {
			lprintf(LOG_INFO,"0000 Waiting for %d active clients to disconnect..."
deuce's avatar
deuce committed
				, protected_uint32_value(active_clients));
			while(protected_uint32_value(active_clients)) {
				if(time(NULL)-start > startup->max_inactivity * 2) {
					lprintf(LOG_WARNING,"0000 !TIMEOUT waiting for %d active clients"
deuce's avatar
deuce committed
						, protected_uint32_value(active_clients));
			lprintf(LOG_INFO, "0000 Done waiting for active clients to disconnect");
rswindell's avatar
rswindell committed
		cleanup(0,__LINE__);
			lprintf(LOG_INFO,"Recycling server...");
			mswait(2000);
			if(startup->recycle!=NULL)
				startup->recycle(startup->cbdata);
	protected_uint32_destroy(thread_count);