Skip to content
Snippets Groups Projects
ftpsrvr.c 152 KiB
Newer Older
	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 */
	if(protected_uint32_value(active_clients))
		lprintf(LOG_WARNING,"#### !FTP Server terminating with %ld 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);
	sscanf("$Revision$", "%*s %s", revision);
		"Compiled %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;
	}

	ZERO_VAR(js_server_props);
	SAFEPRINTF2(js_server_props.version,"%s %s",FTP_SERVER,revision);
	js_server_props.version_detail=ftp_ver();
	js_server_props.clients=&active_clients.value;
	js_server_props.options=&startup->options;
	js_server_props.interfaces=&startup->interfaces;
	startup->recycle_now=FALSE;
	protected_uint32_init(&thread_count, 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");
		if(startup->html_index_file[0]==0)		SAFECOPY(startup->html_index_file,"00index.html");
		if(startup->html_index_script[0]==0)	SAFECOPY(startup->html_index_script,"ftp-html.js");
		if(startup->options&FTP_OPT_HTML_INDEX_FILE)
			startup->options&=~FTP_OPT_NO_JAVASCRIPT;
		else
			startup->options|=FTP_OPT_NO_JAVASCRIPT;
		if(startup->js.max_bytes==0)			startup->js.max_bytes=JAVASCRIPT_MAX_BYTES;
		if(startup->js.cx_stack==0)				startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK;
		protected_uint32_adjust(&thread_count,1);
		memset(&scfg, 0, sizeof(scfg));
		lprintf(LOG_INFO,"Synchronet FTP Server Revision %s%s"
		DESCRIBE_COMPILER(compiler);
		lprintf(LOG_INFO,"Compiled %s %s with %s", __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: %lx"
			,ctime_r(&t,str),startup->options);
		if(chdir(startup->ctrl_dir)!=0)
			lprintf(LOG_ERR,"!ERROR %d changing directory to: %s", 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, TRUE, error)) {
			lprintf(LOG_CRIT,"!ERROR %s",error);
			lprintf(LOG_CRIT,"!Failed to load configuration files");
rswindell's avatar
rswindell committed
			cleanup(1,__LINE__);
			SAFECOPY(startup->host_name,scfg.sys_inetaddr);
		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(!isdir(scfg.temp_dir) && MKDIR(scfg.temp_dir) != 0) {
			lprintf(LOG_ERR, "Error %d creating temp directory: %s", errno, scfg.temp_dir);
			cleanup(1,__LINE__);
			break;
		}
		lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
		if(!isdir(scfg.temp_dir)) {
			lprintf(LOG_CRIT,"!Invalid temp 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);
		protected_uint32_init(&active_clients, 0);
		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);
deuce's avatar
deuce committed
		lprintf(LOG_INFO,"FTP Server listening");
		/* 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);
deuce's avatar
deuce committed
			client_socket = xpms_accept(ftp_set, &client_addr, &client_addr_len, startup->sem_chk_freq*1000, 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(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.");
deuce's avatar
deuce committed
				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"
deuce's avatar
deuce committed
				sockprintf(client_socket,-1,"421 System error, please try again later.");
deuce's avatar
deuce committed
				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;
			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)) {
deuce's avatar
deuce committed
			lprintf(LOG_DEBUG,"Waiting for %d active clients to disconnect..."
				, protected_uint32_value(active_clients));
			while(protected_uint32_value(active_clients)) {
				if(time(NULL)-start>startup->max_inactivity) {
deuce's avatar
deuce committed
					lprintf(LOG_WARNING,"!TIMEOUT waiting for %d active clients"
						, protected_uint32_value(active_clients));
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);