Newer
Older
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
,FALSE /* append */
,desc
);
filepos=0;
continue;
}
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) {
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
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);
#ifdef _WIN32
if(startup->hack_sound[0] && !(startup->options&FTP_OPT_MUTE))
PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
#endif
}
sockprintf(sock,"500 Syntax error: '%s'",cmd);
lprintf("%04d !UNSUPPORTED COMMAND from %s: '%s'"
,sock,user.alias,cmd);
} /* while(1) */
#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;
}

rswindell
committed
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) {
lprintf("%04d Disconnecting due to to inactivity",sock);

rswindell
committed
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 */

rswindell
committed
if(user.number)
logoutuserdat(&scfg, &user, time(NULL), logintime);
if(startup->hangup_sound[0] && !(startup->options&FTP_OPT_MUTE))
PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
if(js_cx!=NULL) {
lprintf("%04d JavaScript: Destroying context",sock);
JS_DestroyContext(js_cx); /* Free Context */
}

rswindell
committed
if(js_runtime!=NULL) {
lprintf("%04d JavaScript: Destroying runtime",sock);

rswindell
committed
JS_DestroyRuntime(js_runtime);
}
status(STATUS_WFC);
active_clients--;
update_clients();
client_off(sock);
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
committed
ftp_close_socket(&sock,__LINE__);
if(pasv_sock!=INVALID_SOCKET)

rswindell
committed
ftp_close_socket(&pasv_sock,__LINE__);
if(data_sock!=INVALID_SOCKET)

rswindell
committed
ftp_close_socket(&data_sock,__LINE__);
}
static void cleanup(int code)
{
if(server_socket!=INVALID_SOCKET)

rswindell
committed
ftp_close_socket(&server_socket,__LINE__);
#ifdef _WINSOCKAPI_
if(WSAInitialized && WSACleanup()!=0)
lprintf("0000 !WSACleanup ERROR %d",ERROR_VALUE);
#ifdef _WIN32
if(socket_mutex!=NULL) {
CloseHandle(socket_mutex);
socket_mutex=NULL;
}
#endif
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];

rswindell
committed
COMPILER_DESC(compiler);
,FTP_VERSION
#ifdef _DEBUG
," Debug"
#else
,""
#endif
,__DATE__, __TIME__, compiler);
return(ver);
}
void DLLCALL ftp_server(void* arg)
char path[MAX_PATH+1];
char error[256];
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;
time_t initialized;

rswindell
committed
fd_set socket_set;

rswindell
committed
struct timeval tv;
startup=(ftp_startup_t*)arg;
if(startup==NULL) {
sbbs_beep(100,500);
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;
recycle_server=TRUE;
do {
status("Initializing");
memset(&scfg, 0, sizeof(scfg));
#ifdef __unix__ /* Ignore "Broken Pipe" signal */
signal(SIGPIPE,SIG_IGN);
lprintf("Synchronet FTP Server Version %s%s"
,FTP_VERSION
#ifdef _DEBUG
," Debug"
COMPILER_DESC(compiler);
lprintf("Compiled %s %s with %s", __DATE__, __TIME__, compiler);
srand(clock()); /* Seed random number generator */
sbbs_random(10); /* Throw away first number */

rswindell
committed
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;
}
}
if(uptime==0)
uptime=time(NULL);
if(!winsock_startup()) {

rswindell
committed
cleanup(1);
return;
}
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;
}
/* 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);
active_clients=0;
update_clients();
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);
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;
}
/*****************************/
/* 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);
initialized=time(NULL);
while(server_socket!=INVALID_SOCKET) {
sprintf(path,"%sftpsrvr.rec",scfg.ctrl_dir);
if(fdate(path)>initialized) {
lprintf("0000 Recycle semaphore detected");
break;
}
/* now wait for connection */
tv.tv_sec=5;
tv.tv_usec=0;

rswindell
committed
FD_ZERO(&socket_set);
FD_SET(server_socket,&socket_set);

rswindell
committed
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;

rswindell
committed
}
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);