Newer
Older
time_t logintime=0;
time_t file_date;
off_t file_size;

rswindell
committed
glob_t g;
node_t node;
client_t client;
struct tm tm;
struct tm cur_tm;

rswindell
committed
JSRuntime* js_runtime=NULL;
JSObject* js_ftp;
js_callback_t js_callback;
CRYPT_SESSION sess = -1;
BOOL got_pbsz = FALSE;
BOOL protection = FALSE;
SetThreadName("sbbs/ftpControl");
thread_up(TRUE /* setuid */);
lastactive=time(NULL);
sock=ftp.socket;
/* Default data port is ctrl port-1 */
lprintf(LOG_DEBUG,"%04d CTRL thread started", sock);
free(arg);
if(startup->answer_sound[0] && !(startup->options&FTP_OPT_MUTE))
PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);

rswindell
committed
transfer_inprogress = FALSE;
transfer_aborted = FALSE;
l=1;
if((i=ioctlsocket(sock, FIONBIO, &l))!=0) {
lprintf(LOG_ERR,"%04d !ERROR %d (%d) disabling socket blocking"
sockprintf(sock,sess,"425 Error %d disabling socket blocking"
thread_down();
return;
}
memset(&user,0,sizeof(user));
lprintf(LOG_INFO,"%04d CTRL connection accepted from: %s port %u"
SAFECOPY(host_name, STR_NO_HOSTNAME);
if(!(startup->options&FTP_OPT_NO_HOST_LOOKUP)) {
getnameinfo(&ftp.client_addr.addr, sizeof(ftp.client_addr), host_name, sizeof(host_name), NULL, 0, NI_NAMEREQD);
lprintf(LOG_INFO,"%04d Hostname: %s [%s]", sock, host_name, host_ip);
ulong banned = loginBanned(&scfg, startup->login_attempt_list, sock, host_name, startup->login_attempt, &attempted);
if(banned || trashcan(&scfg,host_ip,"ip")) {
if(banned) {
char ban_duration[128];
lprintf(LOG_NOTICE, "%04d !TEMPORARY BAN of %s (%lu login attempts, last: %s) - remaining: %s"
,sock, host_ip, attempted.count-attempted.dupes, attempted.user, seconds_to_str(banned, ban_duration));
} else
lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", sock, host_ip);
sockprintf(sock,sess,"550 Access denied.");
ftp_close_socket(&sock,&sess,__LINE__);
thread_down();
return;
}
if(trashcan(&scfg,host_name,"host")) {
lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in host.can: %s", sock, host_name);
sockprintf(sock,sess,"550 Access denied.");
ftp_close_socket(&sock,&sess,__LINE__);
thread_down();
return;
}
/* For PASV mode */
addr_len=sizeof(pasv_addr);
lprintf(LOG_ERR,"%04d !ERROR %d (%d) getting address/port", sock, result, ERROR_VALUE);
sockprintf(sock,sess,"425 Error %d getting address/port",ERROR_VALUE);
ftp_close_socket(&sock,&sess,__LINE__);
thread_down();
return;
}
protected_uint32_adjust(&active_clients, 1);
update_clients();
/* Initialize client display */
client.size=sizeof(client);
client.time=time32(NULL);
SAFECOPY(client.addr,host_ip);
SAFECOPY(client.host,host_name);
client.user=STR_UNKNOWN_USER;
client.usernum = 0;
client_on(sock,&client,FALSE /* update */);
&& (login_attempts=loginAttempts(startup->login_attempt_list, &ftp.client_addr)) > 1) {
lprintf(LOG_DEBUG,"%04d Throttling suspicious connection from: %s (%lu login attempts)"
mswait(login_attempts*startup->login_attempt.throttle);
}
sockprintf(sock,sess,"220-%s (%s)",scfg.sys_name, startup->host_name);
sockprintf(sock,sess," Synchronet FTP Server %s-%s Ready"
,revision,PLATFORM_DESC);

rswindell
committed
sprintf(str,"%sftplogin.txt",scfg.text_dir);
if((fp=fopen(str,"rb"))!=NULL) {
while(!feof(fp)) {
if(!fgets(buf,sizeof(buf),fp))
break;
truncsp(buf);
socket_debug[sock]|=SOCKET_DEBUG_CTRL;
socket_debug[sock]|=SOCKET_DEBUG_READLINE;
rd = sockreadline(sock, sess, buf, sizeof(buf), &lastactive);
socket_debug[sock]&=~SOCKET_DEBUG_READLINE;
if(transfer_inprogress==TRUE) {
lprintf(LOG_WARNING,"%04d <%s> !Aborting transfer due to CTRL socket receive error", sock, user.number ? user.alias : host_ip);
}
truncsp(buf);
lastactive=time(NULL);
cmd=buf;
while(((BYTE)*cmd)==TELNET_IAC) {
cmd++;
lprintf(LOG_DEBUG,"%04d <%s> RX%s: Telnet cmd: %s", sock, user.number ? user.alias : host_ip, sess == -1 ? "" : "S", telnet_cmd_desc(*cmd));
cmd++;
}
while(*cmd && *cmd<' ') {
lprintf(LOG_DEBUG,"%04d <%s> RX%s: %d (0x%02X)",sock, user.number ? user.alias : host_ip, sess == -1 ? "" : "S", (BYTE)*cmd,(BYTE)*cmd);
cmd++;
}
if(!(*cmd))
continue;
if(startup->options&FTP_OPT_DEBUG_RX)
lprintf(LOG_DEBUG,"%04d <%s> RX%s: %s", sock, user.number ? user.alias : host_ip, sess == -1 ? "" : "S", cmd);
continue;
}
if(!stricmp(cmd, "HELP SITE") || !stricmp(cmd, "SITE HELP")) {
sockprintf(sock,sess,"214-The following SITE commands are recognized (* => unimplemented):");
sockprintf(sock,sess," HELP VER WHO UPTIME");
if(user.level>=SYSOP_LEVEL)
" RECYCLE [ALL]");
sockprintf(sock,sess,"214 Direct comments to sysop@%s.",scfg.sys_inetaddr);
continue;
}
if(!strnicmp(cmd, "HELP",4)) {
sockprintf(sock,sess,"214-The following commands are recognized (* => unimplemented, # => extension):");
sockprintf(sock,sess," USER PASS CWD XCWD CDUP XCUP PWD XPWD");
sockprintf(sock,sess," QUIT REIN PORT PASV LIST NLST NOOP HELP");
sockprintf(sock,sess," SIZE MDTM RETR STOR REST ALLO ABOR SYST");
sockprintf(sock,sess," TYPE STRU MODE SITE RNFR* RNTO* DELE* DESC#");
sockprintf(sock,sess," FEAT# OPTS# EPRT EPSV AUTH# PBSZ# PROT# CCC#");
sockprintf(sock,sess," MLSD#");
sockprintf(sock,sess,"214 Direct comments to sysop@%s.",scfg.sys_inetaddr);
continue;
}
if(!stricmp(cmd, "FEAT")) {
sockprintf(sock,sess,"211-The following additional (post-RFC949) features are supported:");
sockprintf(sock,sess," DESC");
sockprintf(sock,sess," MDTM");
sockprintf(sock,sess," SIZE");
sockprintf(sock,sess," REST STREAM");
sockprintf(sock,sess," AUTH TLS");
sockprintf(sock,sess," PBSZ");
sockprintf(sock,sess," PROT");
sockprintf(sock,sess," MLST Type%s;Perm%s;Size%s;Modify%s;UNIX.ownername%s;Unique%s;Create%s",
(mlsx_feats & MLSX_TYPE) ? "*" : "",
(mlsx_feats & MLSX_PERM) ? "*" : "",
(mlsx_feats & MLSX_SIZE) ? "*" : "",
(mlsx_feats & MLSX_MODIFY) ? "*" : "",
(mlsx_feats & MLSX_OWNER) ? "*" : "",
(mlsx_feats & MLSX_UNIQUE) ? "*" : "",
(mlsx_feats & MLSX_CREATE) ? "*" : ""
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
if(!strnicmp(cmd, "OPTS MLST",9)) {
if (cmd[9] == 0) {
mlsx_feats = 0;
continue;
}
if (cmd[9] != ' ') {
sockprintf(sock,sess,"501 Option not supported.");
continue;
}
mlsx_feats = 0;
for (p = cmd; *p; p++)
*p = toupper(*p);
if (strstr(cmd, "TYPE;"))
mlsx_feats |= MLSX_TYPE;
if (strstr(cmd, "PERM;"))
mlsx_feats |= MLSX_PERM;
if (strstr(cmd, "SIZE;"))
mlsx_feats |= MLSX_SIZE;
if (strstr(cmd, "MODIFY;"))
mlsx_feats |= MLSX_MODIFY;
if (strstr(cmd, "UNIX.OWNERNAME;"))
mlsx_feats |= MLSX_OWNER;
if (strstr(cmd, "UNIQUE;"))
mlsx_feats |= MLSX_UNIQUE;
if (strstr(cmd, "CREATE;"))
mlsx_feats |= MLSX_CREATE;
sockprintf(sock,sess,"200 %s%s%s%s%s%s%s",
(mlsx_feats & MLSX_TYPE) ? "Type;" : "",
(mlsx_feats & MLSX_PERM) ? "Perm;" : "",
(mlsx_feats & MLSX_SIZE) ? "Size;" : "",
(mlsx_feats & MLSX_MODIFY) ? "Modify;" : "",
(mlsx_feats & MLSX_OWNER) ? "UNIX.ownername;" : "",
(mlsx_feats & MLSX_UNIQUE) ? "Unique;" : "",
(mlsx_feats & MLSX_CREATE) ? "Create;" : ""
if(!strnicmp(cmd, "OPTS",4)) {
sockprintf(sock,sess,"501 Option not supported.");
continue;
}
if(!stricmp(cmd, "QUIT")) {
ftp_printfile(sock,sess,"bye",221);
sockprintf(sock,sess,"221 Goodbye. Closing control connection.");
break;
}
if(!strnicmp(cmd, "USER ",5)) {
sysop=FALSE;
user.number=0;
p=cmd+5;
SKIP_WHITESPACE(p);
user.number=matchuser(&scfg,user.alias,FALSE /*sysop_alias*/);
if(!user.number && (stricmp(user.alias,"anonymous") == 0 || stricmp(user.alias, "ftp") == 0))
user.number=matchuser(&scfg,"guest",FALSE);
if(user.number && getuserdat(&scfg, &user)==0 && user.pass[0]==0)
sockprintf(sock,sess,"331 User name okay, give your full e-mail address as password.");
continue;
}
if(!strnicmp(cmd, "PASS ",5) && user.alias[0]) {
user.number=0;
p=cmd+5;
SKIP_WHITESPACE(p);
user.number=matchuser(&scfg,user.alias,FALSE /*sysop_alias*/);
if(scfg.sys_misc&SM_ECHO_PW)

rswindell
committed
lprintf(LOG_WARNING,"%04d !UNKNOWN USER: '%s' (password: %s)",sock,user.alias,p);

rswindell
committed
lprintf(LOG_WARNING,"%04d !UNKNOWN USER: '%s'",sock,user.alias);
if(badlogin(sock, sess, &login_attempts, user.alias, p, host_name, &ftp.client_addr))
continue;
}
if((i=getuserdat(&scfg, &user))!=0) {
lprintf(LOG_ERR,"%04d <%s> !ERROR %d getting data for user #%d"
,sock, user.alias, i, user.number);
user.number=0;
continue;
}
if(user.misc&(DELETED|INACTIVE)) {
lprintf(LOG_WARNING,"%04d <%s> !DELETED or INACTIVE user #%d"
,sock,user.alias,user.number);
if(badlogin(sock, sess, &login_attempts, NULL, NULL, NULL, NULL))
continue;
}
if(user.rest&FLAG('T')) {
lprintf(LOG_WARNING,"%04d <%s> !T RESTRICTED user #%d"
,sock,user.alias,user.number);
if(badlogin(sock, sess, &login_attempts, NULL, NULL, NULL, NULL))
if(user.ltoday>=scfg.level_callsperday[user.level]
&& !(user.exempt&FLAG('L'))) {
lprintf(LOG_WARNING,"%04d <%s> !MAXIMUM LOGONS (%d) reached for level %u"
,sock,user.alias,scfg.level_callsperday[user.level], user.level);
user.number=0;
continue;
}
if(user.rest&FLAG('L') && user.ltoday>=1) {
lprintf(LOG_WARNING,"%04d <%s> !L RESTRICTED user already on today"
,sock,user.alias);
user.number=0;
continue;
}
SAFEPRINTF2(sys_pass,"%s:%s",user.pass,scfg.sys_pass);
if(!user.pass[0]) { /* Guest/Anonymous */
if(trashcan(&scfg,password,"email")) {
lprintf(LOG_NOTICE,"%04d <%s> !BLOCKED e-mail address: %s", sock, user.alias, password);
user.number=0;
if(badlogin(sock, sess, &login_attempts, NULL, NULL, NULL, NULL))
continue;
}
lprintf(LOG_INFO,"%04d <%s> identity: %s",sock,user.alias,password);
putuserrec(&scfg,user.number,U_NETMAIL,LEN_NETMAIL,password);
}
else if(user.level>=SYSOP_LEVEL && !stricmp(password,sys_pass)) {
lprintf(LOG_INFO,"%04d <%s> Sysop access granted", sock, user.alias);
sysop=TRUE;
}
else if(stricmp(password,user.pass)) {

rswindell
committed
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_WARNING,"%04d <%s> !FAILED Password attempt: '%s' expected '%s'"

rswindell
committed
,sock, user.alias, password, user.pass);
else
lprintf(LOG_WARNING,"%04d <%s> !FAILED Password attempt"

rswindell
committed
,sock, user.alias);
if(badlogin(sock, sess, &login_attempts, user.alias, password, host_name, &ftp.client_addr))
continue;
}
/* Update client display */
if(user.pass[0]) {
client.user=user.alias;
loginSuccess(startup->login_attempt_list, &ftp.client_addr);
} else { /* anonymous */
sprintf(str,"%s <%.32s>",user.alias,password);
client.user=str;
}
client.usernum = user.number;
client_on(sock,&client,TRUE /* update */);
lprintf(LOG_INFO,"%04d <%s> logged in (%u today, %u total)"
,sock,user.alias,user.ltoday+1, user.logons+1);
timeleft=(long)gettimeleft(&scfg,&user,logintime);
#ifdef JAVASCRIPT
#ifdef JS_CX_PER_SESSION
if(js_CreateUserClass(js_cx, js_glob, &scfg)==NULL)
lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user class",sock);
if(js_CreateUserObject(js_cx, js_glob, &scfg, "user", user.number, &client)==NULL)
lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user object",sock);
if(js_CreateClientObject(js_cx, js_glob, "client", &client, sock, -1)==NULL)
lprintf(LOG_ERR,"%04d !JavaScript ERROR creating client object",sock);
if(js_CreateFileAreaObject(js_cx, js_glob, &scfg, &user
,startup->html_index_file)==NULL)
lprintf(LOG_ERR,"%04d !JavaScript ERROR creating file area object",sock);
#endif
sockprintf(sock,sess,"230-Sysop access granted.");
sockprintf(sock,sess,"230-%s logged in.",user.alias);
if(!(user.exempt&FLAG('D')) && (user.cdt+user.freecdt)>0)
,user.cdt+user.freecdt);
sockprintf(sock,sess,"230 You are allowed %lu minutes of use for this session."

rswindell
committed
sprintf(qwkfile,"%sfile/%04d.qwk",scfg.data_dir,user.number);
/* Adjust User Total Logons/Logons Today */
user.logons++;
user.ltoday++;
SAFECOPY(user.modem,"FTP");
SAFECOPY(user.comp,host_name);
user.logontime=(time32_t)logintime;
putuserdat(&scfg, &user);
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
if (!strnicmp(cmd, "AUTH ", 5)) {
if(!stricmp(cmd, "AUTH TLS")) {
if (sess != -1) {
sockprintf(sock,sess,"534 Already in TLS mode");
continue;
}
if (start_tls(&sock, &sess, TRUE))
break;
user.number=0;
sysop=FALSE;
filepos=0;
got_pbsz = FALSE;
protection = FALSE;
continue;
}
sockprintf(sock,sess,"504 TLS is the only AUTH supported");
continue;
}
if (!strnicmp(cmd, "PBSZ ", 5)) {
if(!stricmp(cmd, "PBSZ 0") && sess != -1) {
got_pbsz = TRUE;
sockprintf(sock,sess,"200 OK");
continue;
}
if (sess == -1) {
sockprintf(sock,sess,"503 Need AUTH TLS first");
continue;
}
if (strspn(cmd+5, "0123456789") == strlen(cmd+5)) {
sockprintf(sock,sess,"200 PBSZ=0");
continue;
}
sockprintf(sock,sess,"501 Unable to parse buffer size");
continue;
}
if (!strnicmp(cmd, "PROT ", 5)) {
if (sess == -1) {
sockprintf(sock,sess,"503 No AUTH yet");
continue;
}
if(!strnicmp(cmd, "PROT P",6) && sess != -1 && got_pbsz) {
protection = TRUE;
sockprintf(sock,sess,"200 Accepted");
continue;
}
if(!strnicmp(cmd, "PROT C",6) && sess != -1 && got_pbsz) {
protection = FALSE;
sockprintf(sock,sess,"200 Accepted");
continue;
}
sockprintf(sock,sess,"536 Only C and P are supported in TLS mode");
continue;
}
if(!stricmp(cmd, "CCC")) {
if (sess == -1) {
sockprintf(sock,sess,"533 Not in TLS mode");
continue;
}
sockprintf(sock,sess,"200 Accepted");
cryptDestroySession(sess);
sess = -1;
continue;
}
sockprintf(sock,sess,"530 Please login with USER and PASS.");
if(!(user.rest&FLAG('G')))
getuserdat(&scfg, &user); /* get current user data */
if((timeleft=(long)gettimeleft(&scfg,&user,logintime))<1L) {
lprintf(LOG_WARNING,"%04d <%s> Out of time, disconnecting",sock, user.alias);
break;
}
/********************************/
/* These commands require login */
/********************************/
if(!stricmp(cmd, "REIN")) {
lprintf(LOG_INFO,"%04d <%s> reinitialized control session",sock,user.alias);
user.number=0;
sysop=FALSE;
filepos=0;
sockprintf(sock,sess,"220 Control session re-initialized. Ready for re-login.");
if (sess != -1) {
cryptDestroySession(sess);
sess = -1;
}
got_pbsz = FALSE;
protection = FALSE;
continue;
}
if(!stricmp(cmd, "SITE WHO")) {
for(i=0;i<scfg.sys_nodes && i<scfg.sys_lastnode;i++) {
if((result=getnodedat(&scfg, i+1, &node, FALSE, NULL))!=0) {
sockprintf(sock,sess," Error %d getting data for Telnet Node %d",result,i+1);
continue;
}
if(node.status==NODE_INUSE)
sockprintf(sock,sess," Node %3d: %s",i+1, username(&scfg,node.useron,str));
sockprintf(sock,sess,"211 End (%d active FTP clients)", protected_uint32_value(active_clients));
continue;
}
if(!stricmp(cmd, "SITE VER")) {
if(!stricmp(cmd, "SITE UPTIME")) {
sockprintf(sock,sess,"211 %s (%lu served)",sectostr((uint)(time(NULL)-uptime),str),served);
continue;
}
if(!stricmp(cmd, "SITE RECYCLE") && user.level>=SYSOP_LEVEL) {
startup->recycle_now=TRUE;
sockprintf(sock,sess,"211 server will recycle when not in-use");
continue;
}
if(!stricmp(cmd, "SITE RECYCLE ALL") && user.level>=SYSOP_LEVEL) {
refresh_cfg(&scfg);
sockprintf(sock,sess,"211 ALL servers/nodes will recycle when not in-use");
continue;
}
if(!strnicmp(cmd,"SITE EXEC ",10) && sysop) {
p=cmd+10;
SKIP_WHITESPACE(p);
#ifdef __unix__
fp=popen(p,"r");
if(fp==NULL)
sockprintf(sock,sess,"500 Error %d opening pipe to: %s",errno,p);
else {
while(!feof(fp)) {
if(fgets(str,sizeof(str),fp)==NULL)
break;
}
#else
sockprintf(sock,sess,"200 system(%s) returned %d",p,system(p));
if(!stricmp(cmd, "SITE DEBUG")) {
for(i=0;i<sizeof(socket_debug);i++)
if(socket_debug[i]!=0)
sockprintf(sock,sess,"211-socket %d = 0x%X",i,socket_debug[i]);
sockprintf(sock,sess,"211 End");
continue;
}
if(strnicmp(cmd, "PORT ",5)==0 || strnicmp(cmd, "EPRT ",5)==0 || strnicmp(cmd, "LPRT ",5)==0) {
if(pasv_sock!=INVALID_SOCKET) {
ftp_close_socket(&pasv_sock,&pasv_sess,__LINE__);
}
SKIP_WHITESPACE(p);
if(strnicmp(cmd, "PORT ",5)==0) {
sscanf(p,"%u,%u,%u,%u,%hd,%hd",&h1,&h2,&h3,&h4,&p1,&p2);
data_addr.in.sin_family=AF_INET;
data_addr.in.sin_addr.s_addr=htonl((h1<<24)|(h2<<16)|(h3<<8)|h4);
char delim = *p;
int prot;
memset(&data_addr, 0, sizeof(data_addr));
if(*p)
p++;
prot=strtol(p,NULL,/* base: */10);
switch(prot) {
case 1:
FIND_CHAR(p,delim);
if(*p)
p++;
ap = p;
old_char = *p;
*p = 0;
data_addr.in.sin_addr.s_addr=inet_addr(ap);
*p = old_char;
if (*p)
p++;
data_port=atoi(p);
data_addr.in.sin_family=AF_INET;
break;
case 2:
FIND_CHAR(p,delim);
if(*p)
p++;
strncpy(addr_str, p, sizeof(addr_str));
addr_str[sizeof(addr_str)-1]=0;
tp=addr_str;
FIND_CHAR(tp, delim);
*tp=0;
if(inet_ptoaddr(addr_str, &data_addr, sizeof(data_addr))==NULL) {
lprintf(LOG_WARNING,"%04d <%s> !Unable to parse IPv6 address: %s",sock, user.alias, addr_str);
sockprintf(sock,sess,"522 Unable to parse IPv6 address (1)");
continue;
}
FIND_CHAR(p,delim);
if(*p)
p++;
data_port=atoi(p);
data_addr.in6.sin6_family=AF_INET6;
break;
default:
lprintf(LOG_WARNING,"%04d <%s> !UNSUPPORTED protocol: %d", sock, user.alias, prot);
sockprintf(sock,sess,"522 Network protocol not supported, use (1)");
continue;
}
}
else { /* LPRT */
if(sscanf(p,"%u,%u",&h1, &h2)!=2) {
lprintf(LOG_ERR, "%04d <%s> !Unable to parse LPRT: %s", sock, user.alias, p);
FIND_CHAR(p,',');
if(*p)
p++;
FIND_CHAR(p,',');
if(*p)
p++;
switch(h1) {
case 4: /* IPv4 */
if(h2 != 4) {
lprintf(LOG_ERR, "%04d <%s> !Unable to parse LPRT: %s", sock, user.alias, p);
sockprintf(sock,sess, "501 IPv4 Address is the wrong length");
continue;
}
for(h1 = 0; h1 < h2; h1++) {
((unsigned char *)(&data_addr.in.sin_addr))[h1]=atoi(p);
FIND_CHAR(p,',');
if(*p)
p++;
}
if(atoi(p)!=2) {
lprintf(LOG_ERR, "%04d <%s> !Unable to parse LPRT %s", sock, user.alias, p);
continue;
}
FIND_CHAR(p,',');
if(*p)
p++;
for(h1 = 0; h1 < 2; h1++) {
((unsigned char *)(&data_port))[1-h1]=atoi(p);
FIND_CHAR(p,',');
if(*p)
p++;
}
data_addr.in.sin_family=AF_INET;
break;
case 6: /* IPv6 */
if(h2 != 16) {
lprintf(LOG_ERR, "%04d <%s> !Unable to parse LPRT: %s",sock, user.alias, p);
sockprintf(sock,sess, "501 IPv6 Address is the wrong length");
continue;
}
for(h1 = 0; h1 < h2; h1++) {
((unsigned char *)(&data_addr.in6.sin6_addr))[h1]=atoi(p);
FIND_CHAR(p,',');
if(*p)
p++;
}
if(atoi(p)!=2) {
lprintf(LOG_ERR, "%04d <%s> !Unable to parse LPRT: %s",sock, user.alias, p);
continue;
}
FIND_CHAR(p,',');
if(*p)
p++;
for(h1 = 0; h1 < 2; h1++) {
((unsigned char *)(&data_port))[1-h1]=atoi(p);
FIND_CHAR(p,',');
if(*p)
p++;
}
data_addr.in6.sin6_family=AF_INET6;
break;
default:
lprintf(LOG_ERR, "%04d <%s> !Unable to parse LPRT: %s",sock, user.alias, p);
inet_addrtop(&data_addr, data_ip, sizeof(data_ip));
if(data_port< IPPORT_RESERVED) {
lprintf(LOG_WARNING,"%04d <%s> !SUSPECTED BOUNCE ATTACK ATTEMPT to %s port %u"
,sock,user.alias
ftp_hacklog("FTP BOUNCE", user.alias, cmd, host_name, &ftp.client_addr);
continue; /* As recommended by RFC2577 */
}
mode="active";
if(stricmp(cmd, "PASV")==0 || stricmp(cmd, "P@SW")==0 /* Kludge required for SMC Barricade V1.2 */
|| stricmp(cmd, "EPSV")==0 || strnicmp(cmd, "EPSV ", 5)==0 || stricmp(cmd, "LPSV")==0) {
if(pasv_sock!=INVALID_SOCKET)
ftp_close_socket(&pasv_sock,&pasv_sess,__LINE__);
if((pasv_sock=ftp_open_socket(pasv_addr.addr.sa_family, SOCK_STREAM))==INVALID_SOCKET) {
lprintf(LOG_WARNING,"%04d <%s> !PASV ERROR %d opening socket", sock, user.alias, ERROR_VALUE);
sockprintf(sock,sess,"425 Error %d opening PASV data socket", ERROR_VALUE);
reuseaddr=FALSE;
if((result=setsockopt(pasv_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&reuseaddr,sizeof(reuseaddr)))!=0) {
lprintf(LOG_WARNING,"%04d <%s> !PASV ERROR %d disabling REUSEADDR socket option"
,sock, user.alias, ERROR_VALUE);
sockprintf(sock,sess,"425 Error %d disabling REUSEADDR socket option", ERROR_VALUE);
continue;
}
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf(LOG_DEBUG,"%04d <%s> PASV DATA socket %d opened",sock, user.alias, pasv_sock);
for(port=startup->pasv_port_low; port<=startup->pasv_port_high; port++) {
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf(LOG_DEBUG,"%04d <%s> PASV DATA trying to bind socket to port %u"
,sock, user.alias, port);
if((result=bind(pasv_sock, &pasv_addr.addr,xp_sockaddr_len(&pasv_addr)))==0)
break;
if(port==startup->pasv_port_high)
break;
lprintf(LOG_ERR,"%04d <%s> !PASV ERROR %d (%d) binding socket to port %u"
,sock, user.alias, result, ERROR_VALUE, port);
sockprintf(sock,sess,"425 Error %d binding data socket",ERROR_VALUE);
ftp_close_socket(&pasv_sock,&pasv_sess,__LINE__);
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf(LOG_DEBUG,"%04d <%s> PASV DATA socket %d bound to port %u",sock, user.alias, pasv_sock, port);
lprintf(LOG_ERR,"%04d <%s> !PASV ERROR %d (%d) getting address/port"
,sock, user.alias, result, ERROR_VALUE);
sockprintf(sock,sess,"425 Error %d getting address/port",ERROR_VALUE);
ftp_close_socket(&pasv_sock,&pasv_sess,__LINE__);
continue;
}
if((result=listen(pasv_sock, 1))!= 0) {
lprintf(LOG_ERR,"%04d <%s> !PASV ERROR %d (%d) listening on port %u"
,sock, user.alias, result, ERROR_VALUE,port);
sockprintf(sock,sess,"425 Error %d listening on data socket",ERROR_VALUE);
ftp_close_socket(&pasv_sock,&pasv_sess,__LINE__);
if(strnicmp(cmd, "EPSV", 4)==0)
sockprintf(sock,sess,"229 Entering Extended Passive Mode (|||%hu|)", port);
else if (stricmp(cmd,"LPSV")==0) {
switch(addr.addr.sa_family) {
case AF_INET:
sockprintf(sock,sess, "228 Entering Long Passive Mode (4, 4, %d, %d, %d, %d, 2, %d, %d)"
,((unsigned char *)&(addr.in.sin_addr))[0]
,((unsigned char *)&(addr.in.sin_addr))[1]
,((unsigned char *)&(addr.in.sin_addr))[2]
,((unsigned char *)&(addr.in.sin_addr))[3]
,((unsigned char *)&(addr.in.sin_port))[0]
,((unsigned char *)&(addr.in.sin_port))[1]);
break;
case AF_INET6:
sockprintf(sock,sess, "228 Entering Long Passive Mode (6, 16, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, 2, %d, %d)"
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
,((unsigned char *)&(addr.in6.sin6_addr))[0]
,((unsigned char *)&(addr.in6.sin6_addr))[1]
,((unsigned char *)&(addr.in6.sin6_addr))[2]
,((unsigned char *)&(addr.in6.sin6_addr))[3]
,((unsigned char *)&(addr.in6.sin6_addr))[4]
,((unsigned char *)&(addr.in6.sin6_addr))[5]
,((unsigned char *)&(addr.in6.sin6_addr))[6]
,((unsigned char *)&(addr.in6.sin6_addr))[7]
,((unsigned char *)&(addr.in6.sin6_addr))[8]
,((unsigned char *)&(addr.in6.sin6_addr))[9]
,((unsigned char *)&(addr.in6.sin6_addr))[10]
,((unsigned char *)&(addr.in6.sin6_addr))[11]
,((unsigned char *)&(addr.in6.sin6_addr))[12]
,((unsigned char *)&(addr.in6.sin6_addr))[13]
,((unsigned char *)&(addr.in6.sin6_addr))[14]
,((unsigned char *)&(addr.in6.sin6_addr))[15]
,((unsigned char *)&(addr.in6.sin6_port))[0]
,((unsigned char *)&(addr.in6.sin6_port))[1]);
break;
}
}
else {
/* Choose IP address to use in passive response */
ip_addr=0;
/* TODO: IPv6 this here lookup */
if(startup->options&FTP_OPT_LOOKUP_PASV_IP
&& (host=gethostbyname(startup->host_name))!=NULL)
ip_addr=ntohl(*((ulong*)host->h_addr_list[0]));
if(ip_addr==0 && (ip_addr=startup->pasv_ip_addr.s_addr)==0)
ip_addr=ntohl(pasv_addr.in.sin_addr.s_addr);
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf(LOG_INFO,"%04d <%s> PASV DATA IP address in response: %u.%u.%u.%u (subject to NAT)"
,user.alias
,(ip_addr>>24)&0xff
,(ip_addr>>16)&0xff
,(ip_addr>>8)&0xff
,ip_addr&0xff
);
sockprintf(sock,sess,"227 Entering Passive Mode (%u,%u,%u,%u,%hu,%hu)"
,(ip_addr>>24)&0xff
,(ip_addr>>16)&0xff
,(ip_addr>>8)&0xff
,ip_addr&0xff
,(ushort)((port>>8)&0xff)
,(ushort)(port&0xff)
mode="passive";
continue;
}
if(!strnicmp(cmd, "TYPE ",5)) {
continue;
}
if(!strnicmp(cmd, "ALLO",4)) {
p=cmd+5;
SKIP_WHITESPACE(p);
if(*p)
l=atol(p);
else
l=0;
if(local_fsys)
avail=getfreediskspace(local_dir,0);
avail=getfreediskspace(scfg.data_dir,0); /* Change to temp_dir? */
continue;
}
if(!strnicmp(cmd, "REST",4)) {
p=cmd+4;
SKIP_WHITESPACE(p);
if(*p)
filepos=atol(p);
else
filepos=0;
sockprintf(sock,sess,"350 Restarting at %"PRIdOFF". Send STORE or RETRIEVE to initiate transfer."
continue;
}
if(!strnicmp(cmd, "MODE ",5)) {
p=cmd+5;
SKIP_WHITESPACE(p);
continue;
}
if(!strnicmp(cmd, "STRU ",5)) {
p=cmd+5;
SKIP_WHITESPACE(p);
continue;
}
if(!stricmp(cmd, "SYST")) {
continue;
}
if(!stricmp(cmd, "ABOR")) {
if(!transfer_inprogress)
sockprintf(sock,sess,"226 No transfer in progress.");
lprintf(LOG_WARNING,"%04d <%s> aborting transfer"
,sock,user.alias);
YIELD(); /* give send thread time to abort */
}
continue;
}
if(!strnicmp(cmd,"SMNT ",5) && sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
p=cmd+5;
SKIP_WHITESPACE(p);
if(!stricmp(p,BBS_FSYS_DIR))
local_fsys=FALSE;
else {
if(!direxist(p)) {
lprintf(LOG_WARNING,"%04d <%s> !attempted to mount invalid directory: %s"
,sock, user.alias, p);
continue;
}
local_fsys=TRUE;
,local_fsys ? "Local" : "BBS");
lprintf(LOG_INFO,"%04d <%s> mounted %s file system"
,sock, user.alias, local_fsys ? "local" : "BBS");
continue;
}
/****************************/
/* Local File System Access */
/****************************/
if(sysop && local_fsys && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
if(local_dir[0]
&& local_dir[strlen(local_dir)-1]!='\\'
&& local_dir[strlen(local_dir)-1]!='/')
strcat(local_dir,"/");
if(!strnicmp(cmd, "MLS", 3)) {
if (cmd[3] == 'T' || cmd[3] == 'D') {
if (cmd[3] == 'D') {
if((fp=fopen(ftp_tmpfname(fname,"lst",sock),"w+b"))==NULL) {
lprintf(LOG_ERR,"%04d <%s> !ERROR %d (%s) line %d opening %s"
,sock, user.alias, errno, strerror(errno), __LINE__, fname);
sockprintf(sock,sess, "451 Insufficient system storage");
continue;
}
}
p=cmd+4;
SKIP_WHITESPACE(p);
filespec=p;
if (!local_dir[0])
strcpy(local_dir, "/");