Newer
Older
//*(p++) = 'm'; // New subdirectories may be created
//*(p++) = 'p'; // Files/Dirs in directory may be deleted
if (can_download(lib, dir, user, client, file))
*(p++) = 'r'; // File may be retrieved
//*(p++) = 'w'; // File may be overwritten
*p = 0;
}
static void get_owner_name(file_t *file, char *namestr)
{
char *p;
if (file) {
if (file->hdr.attr & MSG_ANONYMOUS)
strcpy(namestr, ANONYMOUS);
else
strcpy(namestr, file->from);
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
}
else
strcpy(namestr, scfg.sys_id);
// Now ensure it's an RCHAR string.
for (p=namestr; *p; p++) {
if (*p >= '!' && *p <= ')')
continue;
else if (*p >= '+' && *p <= ':')
continue;
else if (*p >= '?' && *p <= 'Z')
continue;
else if (*p == '\\')
continue;
else if (*p == '^')
continue;
else if (*p == '_')
continue;
else if (*p >= 'a' && *p <= 'z')
continue;
else if (*p == ' ')
*p = '.';
else
*p = '_';
}
}
static void ctrl_thread(void* arg)
{
unsigned mlsx_feats = (MLSX_TYPE | MLSX_PERM | MLSX_SIZE | MLSX_MODIFY | MLSX_OWNER | MLSX_UNIQUE | MLSX_CREATE);
char buf[512];
char str[128];
char uniq[33];
char* cmd;
char* p;
char* np;
char* tp;
char* dp;
char* ap;
char* filespec;
char* mode="active";
char old_char;
char fname[MAX_PATH+1];
char qwkfile[MAX_PATH+1];
char aliasfile[MAX_PATH+1];
char aliaspath[MAX_PATH+1];
char mls_path[MAX_PATH+1];
char *mls_fname;
char permstr[11];
char aliasline[512];
char desc[501]="";
char sys_pass[128];
char host_name[256];
char host_ip[INET6_ADDRSTRLEN];
char data_ip[INET6_ADDRSTRLEN];
uint16_t data_port;
char path[MAX_PATH+1];
char local_dir[MAX_PATH+1];
char ren_from[MAX_PATH+1]="";

sbbs
committed
unsigned h1,h2,h3,h4;
u_short p1,p2; /* For PORT command */
int i;
int rd;
int result;
int lib;
int dir;
int curlib=-1;
int curdir=-1;
int orglib;
int orgdir;
long filepos=0L;
long timeleft;
ulong l;
ulong login_attempts=0;
ulong avail; /* disk space */
ulong count;
BOOL detail;
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;
off_t file_size;
node_t node;
client_t client;
struct tm tm;
struct tm cur_tm;
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->sound.answer[0] && !sound_muted(&scfg))
PlaySound(startup->sound.answer, 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;
}
(void)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, server_host_name());
sockprintf(sock,sess," Synchronet FTP Server %s%c-%s Ready"
,VERSION, 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) ? "*" : ""
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
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);
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);
#ifdef _WIN32
if(startup->sound.login[0] && !sound_muted(&scfg))
PlaySound(startup->sound.login, NULL, SND_ASYNC|SND_FILENAME);
#endif
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) || sess == -1) {
lprintf(LOG_WARNING, "%04d <%s> failed to initialize TLS successfully", sock, host_ip);
}
user.number=0;
sysop=FALSE;
filepos=0;
got_pbsz = FALSE;
protection = FALSE;
lprintf(LOG_INFO, "%04d <%s> initialized TLS successfully", sock, host_ip);
client.protocol = "FTPS";
client_on(sock, &client, /* update: */TRUE);
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
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__);
}
memcpy(&data_addr, &ftp.client_addr, ftp.client_addr_len);
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);
bool bounce_allowed = (startup->options & FTP_OPT_ALLOW_BOUNCE) && !(user.rest & FLAG('G'));
if(data_port < IPPORT_RESERVED
|| (memcmp(&data_addr, &ftp.client_addr, ftp.client_addr_len) != 0 && !bounce_allowed)) {
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)"
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
,((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(server_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);