Skip to content
Snippets Groups Projects
ftpsrvr.c 121 KiB
Newer Older
		if(!stricmp(cmd,"CDUP") || !stricmp(cmd,"XCUP")) {
			if(curdir<0)
				curlib=-1;
			else
				curdir=-1;
			sockprintf(sock,"200 CDUP command successful.");
			continue;
		}

		if(!strnicmp(cmd, "CWD ", 4) || !strnicmp(cmd,"XCWD ",5)) {
			p=cmd+4;
			while(*p && *p<=' ') p++;

			if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR))) 
				p+=strlen(BBS_FSYS_DIR);	/* already mounted */

			if(*p=='/') {
				curlib=-1;
				curdir=-1;
				p++;
			}
			/* Local File System? */
			if(sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS) 
				&& !strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) {	
				p+=strlen(LOCAL_FSYS_DIR);
				if(!direxist(p)) {
					sockprintf(sock,"550 Directory does not exist.");
					lprintf("%04d !%s attempted to mount invalid directory: %s"
						,sock, user.alias, p);
					continue;
				}
				sprintf(local_dir,"%.*s",(int)sizeof(local_dir)-1,p);
				local_fsys=TRUE;
				sockprintf(sock,"250 CWD command successful (local file system mounted).");
				lprintf("%04d %s mounted local file system", sock, user.alias);
				continue;
			}
			success=FALSE;

			/* Directory Alias? */
			if(curlib<0 && ftpalias(p,NULL,&user,&curdir)==TRUE) {
				if(curdir>=0)
					curlib=scfg.dir[curdir]->lib;
				success=TRUE;
			}

			orglib=curlib;
			orgdir=curdir;
			tp=0;
			if(!strncmp(p,"...",3)) {
				curlib=-1;
				curdir=-1;
				p+=3;
			}
			if(!strncmp(p,"./",2))
				p+=2;
			else if(!strncmp(p,"..",2)) {
				if(curdir<0)
					curlib=-1;
				else
					curdir=-1;
				p+=2;
			}
			if(*p==0)
				success=TRUE;
			else if(!strcmp(p,".")) 
				success=TRUE;
			if(!success  && (curlib<0 || *p=='/')) { /* Root dir */
				if(*p=='/') p++;
				tp=strchr(p,'/');
				if(tp) *tp=0;
				for(i=0;i<scfg.total_libs;i++) {
					if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
						continue;
					if(!stricmp(scfg.lib[i]->sname,p))
						break;
				}
				if(i<scfg.total_libs) {
					curlib=i;
					success=TRUE;
				}
			}
			if((!success && curdir<0) || (success && tp && *(tp+1))) {
				if(tp)
					p=tp+1;
				tp=strchr(p,'/');
				if(tp) *tp=0;
				for(i=0;i<scfg.total_dirs;i++) {
					if(scfg.dir[i]->lib!=curlib)
						continue;
					if(i!=scfg.sysop_dir && i!=scfg.upload_dir
						&& !chk_ar(&scfg,scfg.dir[i]->ar,&user))
						continue;
					if(!stricmp(scfg.dir[i]->code,p))
						break;
				}
				if(i<scfg.total_dirs) {
					curdir=i;
					success=TRUE;
				} else
					success=FALSE;
			}

			if(success)
				sockprintf(sock,"250 CWD command successful.");
			else {
				sockprintf(sock,"550 %s: No such file or directory.",p);
				curlib=orglib;
				curdir=orgdir;
			}
			continue;
		}

		if(!stricmp(cmd, "PWD") || !stricmp(cmd,"XPWD")) {
			if(curlib<0)
				sockprintf(sock,"257 \"/\" is current directory.");
			else if(curdir<0)
				sockprintf(sock,"257 \"/%s\" is current directory."
					,scfg.lib[curlib]->sname);
			else
				sockprintf(sock,"257 \"/%s/%s\" is current directory."
					,scfg.lib[curlib]->sname,scfg.dir[curdir]->code);
			continue;
		}

		if(!strnicmp(cmd, "MKD", 3) || 
			!strnicmp(cmd,"XMKD",4) || 
			!strnicmp(cmd,"SITE EXEC",9)) {
			lprintf("%04d !SUSPECTED HACK ATTEMPT by %s: '%s'"
				,sock,user.alias,cmd);
			hacklog(&scfg, "FTP", user.alias, cmd, host_name, &ftp.client_addr);
			if(startup->hack_sound[0] && !(startup->options&FTP_OPT_MUTE)) 
				PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
		sockprintf(sock,"500 Syntax error: '%s'",cmd);
		lprintf("%04d !UNSUPPORTED COMMAND from %s: '%s'"
			,sock,user.alias,cmd);
#if defined(_DEBUG) && defined(SOCKET_DEBUG_TERMINATE)
	socket_debug[sock]|=SOCKET_DEBUG_TERMINATE;
#endif

	if(transfer_inprogress==TRUE) {
		lprintf("%04d Waiting for transfer to complete...",sock);
		while(transfer_inprogress==TRUE) {
			if(server_socket==INVALID_SOCKET) {
				mswait(2000);	/* allow xfer threads to terminate */
				break;
			}
			if(!transfer_aborted) {
				if(gettimeleft(&scfg,&user,logintime)<1) {
					lprintf("%04d Out of time, disconnecting",sock);
					sockprintf(sock,"421 Sorry, you've run out of time.");
					ftp_close_socket(&data_sock,__LINE__);
					transfer_aborted=TRUE;
				}
				if((time(NULL)-lastactive)>startup->max_inactivity) {
rswindell's avatar
rswindell committed
					lprintf("%04d Disconnecting due to to inactivity",sock);
					sockprintf(sock,"421 Disconnecting due to inactivity (%u seconds)."
						,startup->max_inactivity);
					ftp_close_socket(&data_sock,__LINE__);
					transfer_aborted=TRUE;
				}
		}
		lprintf("%04d Done waiting for transfer to complete",sock);
	}

	/* Update User Statistics */
		logoutuserdat(&scfg, &user, time(NULL), logintime);
rswindell's avatar
rswindell committed
		lprintf("%04d %s logged off",sock,user.alias);
rswindell's avatar
rswindell committed
#ifdef _WIN32
	if(startup->hangup_sound[0] && !(startup->options&FTP_OPT_MUTE)) 
		PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
rswindell's avatar
rswindell committed
#endif
#ifdef JAVASCRIPT
	if(js_cx!=NULL) {
		lprintf("%04d JavaScript: Destroying context",sock);
		JS_DestroyContext(js_cx);	/* Free Context */
	}
		lprintf("%04d JavaScript: Destroying runtime",sock);
	status(STATUS_WFC);

	active_clients--;
	update_clients();
	client_off(sock);
rswindell's avatar
rswindell committed

	thread_down();

	lprintf("%04d CTRL thread terminated (%u clients, %u threads remain)"
		,sock, active_clients, thread_count);
	socket_debug[sock]&=~SOCKET_DEBUG_CTRL;
#if defined(_DEBUG) && defined(SOCKET_DEBUG_TERMINATE)
	socket_debug[sock]&=~SOCKET_DEBUG_TERMINATE;
#endif
rswindell's avatar
rswindell committed

	/* Free up resources here (MUST BE LAST) */
	if(pasv_sock!=INVALID_SOCKET)
	if(data_sock!=INVALID_SOCKET)
}

static void cleanup(int code)
{
	free_cfg(&scfg);

	if(server_socket!=INVALID_SOCKET)
	server_socket=INVALID_SOCKET;

	update_clients();
	if(WSAInitialized && WSACleanup()!=0) 
		lprintf("0000 !WSACleanup ERROR %d",ERROR_VALUE);
#ifdef _WIN32
	if(socket_mutex!=NULL) {
		CloseHandle(socket_mutex);
		socket_mutex=NULL;
	}
#endif

rswindell's avatar
rswindell committed
	thread_down();
	status("Down");
rswindell's avatar
rswindell committed
    lprintf("#### FTP Server thread terminated (%u threads remain)", thread_count);
	if(startup!=NULL && startup->terminated!=NULL)
		startup->terminated(code);
}

const char* DLLCALL ftp_ver(void)
{
	static char ver[256];
	char compiler[32];

	sprintf(ver,"%s v%s%s  "
		"Compiled %s %s with %s"
		,FTP_SERVER
		,FTP_VERSION
#ifdef _DEBUG
		," Debug"
#else
		,""
#endif
		,__DATE__, __TIME__, compiler);

	return(ver);
}

	char			compiler[32];
	SOCKADDR_IN		server_addr;
	SOCKADDR_IN		client_addr;
	int				client_addr_len;
	SOCKET			client_socket;
	int				i;
	int				result;
	time_t			t;
	time_t			start;
	LINGER			linger;

	startup=(ftp_startup_t*)arg;

    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;
	}

	/* Setup intelligent defaults */
	if(startup->port==0)					startup->port=IPPORT_FTP;
	if(startup->qwk_timeout==0)				startup->qwk_timeout=600;		/* seconds */
	if(startup->max_inactivity==0)			startup->max_inactivity=300;	/* seconds */
	if(startup->index_file_name[0]==0)		strcpy(startup->index_file_name,"00index");
	if(startup->html_index_file[0]==0)		strcpy(startup->html_index_file,"00index.html");
	if(startup->html_index_script[0]==0) {	strcpy(startup->html_index_script,"ftp-html.js");
											startup->options|=FTP_OPT_HTML_INDEX_FILE;
	}
	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;
		thread_up(FALSE /* setuid */);
		memset(&scfg, 0, sizeof(scfg));
	#ifdef __unix__		/* Ignore "Broken Pipe" signal */
		signal(SIGPIPE,SIG_IGN);
	#endif
		lprintf("Synchronet FTP Server Version %s%s"
			,FTP_VERSION
	#ifdef _DEBUG
			," Debug"
	#else
			,""
	#endif
			);
		lprintf("Compiled %s %s with %s", __DATE__, __TIME__, compiler);
		srand(clock());		/* Seed random number generator */
		sbbs_random(10);	/* Throw away first number */
		if(!(startup->options&FTP_OPT_LOCAL_TIMEZONE)) { 
			if(PUTENV("TZ=UTC0"))
				lprintf("!putenv() FAILED");
			tzset();

			if((t=checktime())!=0) {   /* Check binary time */
				lprintf("!TIME PROBLEM (%ld)",t);
				cleanup(1);
				return;
			}
		}

		uptime=time(NULL);

		if(!winsock_startup()) {
		t=time(NULL);
		lprintf("Initializing on %.24s with options: %lx"
			,ctime(&t),startup->options);
	#ifdef _WIN32
		if((socket_mutex=CreateMutex(NULL,FALSE,NULL))==NULL) {
    		lprintf("!ERROR %d creating socket_mutex", GetLastError());
			cleanup(1);
			return;
		}
	#endif
		/* Initial configuration and load from CNF files */
		sprintf(scfg.ctrl_dir, "%.*s",(int)sizeof(scfg.ctrl_dir)-1
    		,startup->ctrl_dir);
		lprintf("Loading configuration files from %s", scfg.ctrl_dir);
		scfg.size=sizeof(scfg);
		if(!load_cfg(&scfg, NULL, TRUE, error)) {
			lprintf("!ERROR %s",error);
			lprintf("!Failed to load configuration files");
			cleanup(1);
			return;
		}
		/* Use DATA/TEMP for temp dir - should ch'd to be FTP/HOST specific */
		prep_dir(scfg.data_dir, scfg.temp_dir);
		if(!startup->max_clients) {
			startup->max_clients=scfg.sys_nodes;
			if(startup->max_clients<10)
				startup->max_clients=10;
		}
		lprintf("Maximum clients: %d",startup->max_clients);
		lprintf("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);
		}
		for(i=0;i<scfg.total_dirs;i++) 
			strlwr(scfg.dir[i]->code);
		/* open a socket and wait for a client */
		if((server_socket=ftp_open_socket(SOCK_STREAM))==INVALID_SOCKET) {
			lprintf("!ERROR %d opening socket", ERROR_VALUE);
			cleanup(1);
			return;
		}
		lprintf("%04d FTP socket opened",server_socket);
	#if 1
		linger.l_onoff=TRUE;
		linger.l_linger=5;	/* seconds */
		if((result=setsockopt(server_socket, SOL_SOCKET, SO_LINGER
    		,(char *)&linger, sizeof(linger)))!=0) {
			lprintf ("%04d !ERROR %d (%d) setting socket options."
				,server_socket, result, ERROR_VALUE);
			cleanup(1);
			return;
		}
	#endif
		/*****************************/
		/* Listen for incoming calls */
		/*****************************/
		memset(&server_addr, 0, sizeof(server_addr));
		server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
		server_addr.sin_family = AF_INET;
		server_addr.sin_port   = htons(startup->port);
		if((result=bind(server_socket, (struct sockaddr *) &server_addr
    		,sizeof(server_addr)))!=0) {
			lprintf("%04d !ERROR %d (%d) binding socket to port %u"
				,server_socket, result, ERROR_VALUE,startup->port);
			lprintf("%04d %s", server_socket, BIND_FAILURE_HELP);
			cleanup(1);
			return;
		}
		if((result=listen(server_socket, 1))!= 0) {
			lprintf("%04d !ERROR %d (%d) listening on socket"
				,server_socket, result, ERROR_VALUE);
			cleanup(1);
			return;
		}
		if(startup->setuid!=NULL)
			startup->setuid();

		/* signal caller that we've started up successfully */
		if(startup->started!=NULL)
    		startup->started();
		lprintf("%04d FTP Server thread started on port %d",server_socket,startup->port);
		status(STATUS_WFC);
		while(server_socket!=INVALID_SOCKET) {
			FD_ZERO(&socket_set);
			FD_SET(server_socket,&socket_set);
			if((i=select(server_socket+1,&socket_set,NULL,NULL,&tv))<1) {
				if(i==0) {
					mswait(1);
					continue;
				}
				if(ERROR_VALUE==EINTR)
					lprintf("0000 FTP Server listening interrupted");
				else if(ERROR_VALUE == ENOTSOCK)
            		lprintf("0000 FTP Server sockets closed");
				else
					lprintf("0000 !ERROR %d selecting sockets",ERROR_VALUE);
				break;
			client_addr_len = sizeof(client_addr);
			client_socket = accept(server_socket, (struct sockaddr *)&client_addr
        		,&client_addr_len);
			if(client_socket == INVALID_SOCKET)
			{
				if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINTR) 
            		lprintf("0000 FTP socket closed while listening");
				else
					lprintf("0000 !ERROR %d accepting connection", ERROR_VALUE);
				break;
			}
			if(startup->socket_open!=NULL)
				startup->socket_open(TRUE);
			sockets++;

			if(active_clients>=startup->max_clients) {
				lprintf("%04d !MAXMIMUM CLIENTS (%d) reached, access denied"
					,client_socket, startup->max_clients);
				sockprintf(client_socket,"421 Maximum active clients reached, please try again later.");
				mswait(3000);
				ftp_close_socket(&client_socket,__LINE__);
				continue;
			}
			if((ftp=malloc(sizeof(ftp_t)))==NULL) {
				lprintf("%04d !ERROR allocating %d bytes of memory for ftp_t"
					,client_socket,sizeof(ftp_t));
				sockprintf(client_socket,"421 System error, please try again later.");
				mswait(3000);
				ftp_close_socket(&client_socket,__LINE__);
				continue;
			}
			ftp->socket=client_socket;
			ftp->client_addr=client_addr;
			_beginthread (ctrl_thread, 0, ftp);
		}
		if(active_clients) {
			lprintf("0000 Waiting for %d active clients to disconnect...", active_clients);
			start=time(NULL);
			while(active_clients) {
				if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
					lprintf("0000 !TIMEOUT waiting for %d active clients ",active_clients);
					break;
				}
				mswait(100);
		cleanup(0);

		if(recycle_server) 
			lprintf("Recycling server...");

	} while(recycle_server);