Newer
Older
BOOL success;
BOOL getdate;
BOOL getsize;

rswindell
committed
BOOL delecmd;
BOOL delfile;
BOOL tmpfile;
BOOL credits;
BOOL filedat=FALSE;
BOOL transfer_inprogress;
BOOL transfer_aborted;
BOOL sysop=FALSE;
BOOL local_fsys=FALSE;
BOOL alias_dir;
BOOL reuseaddr;
FILE* fp;
FILE* alias_fp;
SOCKET sock;
SOCKET tmp_sock;
SOCKET pasv_sock=INVALID_SOCKET;
SOCKET data_sock=INVALID_SOCKET;
union xp_sockaddr addr;
union xp_sockaddr data_addr;
union xp_sockaddr pasv_addr;
ftp_t ftp=*(ftp_t*)arg;
user_t user;
time_t t;
time_t now;
time_t logintime=0;
time_t file_date;

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_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) {
if(user.number)
lprintf(LOG_WARNING,"%04d <%s> !Aborting transfer due to CTRL socket receive error", sock, user.alias);
else
lprintf(LOG_WARNING,"%04d !Aborting transfer due to CTRL socket receive error", sock);
}
truncsp(buf);
lastactive=time(NULL);
cmd=buf;
while(((BYTE)*cmd)==TELNET_IAC) {
cmd++;
lprintf(LOG_DEBUG,"%04d RX%s: Telnet cmd: %s",sock,sess == -1 ? "" : "S", telnet_cmd_desc(*cmd));
cmd++;
}
while(*cmd && *cmd<' ') {
lprintf(LOG_DEBUG,"%04d RX%s: %d (0x%02X)",sock,sess == -1 ? "" : "S", (BYTE)*cmd,(BYTE)*cmd);
cmd++;
}
if(!(*cmd))
continue;
if(startup->options&FTP_OPT_DEBUG_RX)
lprintf(LOG_DEBUG,"%04d RX%s: %s", sock, 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) ? "*" : ""
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
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: <%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_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);
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
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
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)"
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
,((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
,(port>>8)&0xff
,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 %lu. 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");