Newer
Older
/* Synchronet command shell/module TCP/IP Network functions */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include "sbbs.h"
#include "cmdshell.h"
#define TIMEOUT_SOCK_READLINE 30 /* seconds */

rswindell
committed
#define TIMEOUT_SOCK_LISTEN 30 /* seconds */
#define TIMEOUT_FTP_RESPONSE 300 /* seconds */
int sbbs_t::exec_net(csi_t* csi)
{
char str[512],rsp[512],buf[1025],ch,*p,**pp,**pp1,**pp2;

Rob Swindell
committed
int i;
switch(*(csi->ip++)) { /* sub-op-code stored as next byte */
case CS_SOCKET_OPEN:
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(csi->sockets>=MAX_SOCKETS)
return(0);
if(lp!=NULL) {
SOCKET sock=open_socket(PF_INET, SOCK_STREAM, NULL);
if(sock!=INVALID_SOCKET) {
SOCKADDR_IN addr;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
if((i=bind(sock, (struct sockaddr *) &addr, sizeof (addr)))!=0) {
csi->socket_error=ERROR_VALUE;
close_socket(sock);
return(0);
}
*lp=sock;
for(i=0;i<csi->sockets;i++)
if(!csi->socket[i])
break;
csi->socket[i]=*lp;
if(i==csi->sockets)
csi->sockets++;
csi->logic=LOGIC_TRUE;
}
}
return(0);
case CS_SOCKET_CLOSE:
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(lp && *lp) {
csi->logic=close_socket((SOCKET)*lp);
csi->socket_error=ERROR_VALUE;
for(i=0;i<csi->sockets;i++)
csi->socket[i]=0;
*lp=0;
}
return(0);
case CS_SOCKET_CHECK:
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(lp==NULL || *lp==INVALID_SOCKET)
return(0);
if(socket_check(*lp,NULL,NULL,0)==TRUE)
csi->logic=LOGIC_TRUE;
else
csi->socket_error=ERROR_VALUE;
return(0);
case CS_SOCKET_CONNECT:
csi->ip+=4;
w=*(ushort *)csi->ip; /* port */
csi->ip+=2;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !*lp || !pp || !*pp || !w)
return(0);
ulong ip_addr;
if((ip_addr=resolve_ip(*pp))==INADDR_NONE)
return(0);
SOCKADDR_IN addr;
memset(&addr,0,sizeof(addr));
addr.sin_addr.s_addr = ip_addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(w);
if((i=connect(*lp, (struct sockaddr *)&addr, sizeof(addr)))!=0) {
csi->socket_error=ERROR_VALUE;
return(0);
}
csi->logic=LOGIC_TRUE;
return(0);
case CS_SOCKET_ACCEPT:
csi->ip+=4;
csi->socket_error=0;
/* TODO */
return(0);
case CS_SOCKET_NREAD:
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp1 || !lp2)
return(0);
if(ioctlsocket(*lp1, FIONREAD, (ulong*)lp2)==0)
csi->logic=LOGIC_TRUE;
else
csi->socket_error=ERROR_VALUE;
return(0);
case CS_SOCKET_PEEK:
case CS_SOCKET_READ:
csi->ip+=4;
w=*(ushort *)csi->ip; /* length */
csi->ip+=2;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp)
return(0);
if(w<1 || w>sizeof(buf)-1)
w=sizeof(buf)-1;
int rcv_count;
if((rcv_count=recv(*lp,buf,w
,*(csi->ip-13)==CS_SOCKET_PEEK ? MSG_PEEK : 0))>0) {
csi->logic=LOGIC_TRUE;
if(csi->etx) {
p=strchr(buf,csi->etx);
if(p) *p=0;
}
*pp=copystrvar(csi,*pp,buf);
} else
csi->socket_error=ERROR_VALUE;
return(0);
csi->ip+=4;
w=*(ushort *)csi->ip; /* length */
csi->ip+=2;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp)
return(0);
if(w<1 || w>sizeof(buf)-1)
w=sizeof(buf)-1;
start=time(NULL);
for(i=0;i<w;) {
if(!online)
return(1);
if(!socket_check(*lp,&rd,NULL,1000))
if(!rd) {
if(time(NULL)-start>TIMEOUT_SOCK_READLINE) {
lprintf(LOG_WARNING,"!socket_readline: timeout (%d) exceeded"
,TIMEOUT_SOCK_READLINE);
return(0);
}
}
if(recv(*lp, &ch, 1, 0)!=1) {
csi->socket_error=ERROR_VALUE;
return(0);
}
if(ch=='\n' && i>=1)
break;
buf[i++]=ch;
if(i>0 && buf[i-1]=='\r')
buf[i-1]=0;
else
buf[i]=0;
if(csi->etx) {
p=strchr(buf,csi->etx);
if(p) *p=0;
}
*pp=copystrvar(csi,*pp,buf);
csi->logic=LOGIC_TRUE;
return(0);
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp || !(*pp))
return(0);
if(sendsocket(*lp,*pp,strlen(*pp))>0)
csi->logic=LOGIC_TRUE;
else
csi->socket_error=ERROR_VALUE;
return(0);
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp1 || !pp2)
return(0);
if(!ftp_cmd(csi,*lp,NULL,rsp))
return(0);
if(atoi(rsp)!=220)
return(0);
sprintf(str,"USER %s",*pp1);
if(!ftp_cmd(csi,*lp,str,rsp))
return(0);
if(atoi(rsp)==331) { /* Password needed */
if(!ftp_cmd(csi,*lp,str,rsp))
return(0);
}
if(atoi(rsp)==230) /* Login successful */
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_LOGOUT:
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp)
return(0);
if(!ftp_cmd(csi,*lp,"QUIT",rsp))
return(0);
if(atoi(rsp)==221) /* Logout successful */
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_PWD:
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp)
return(0);
if(!ftp_cmd(csi,*lp,"PWD",rsp))
return(0);
if(atoi(rsp)==257) /* pathname */
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_CWD:
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp)
return(0);
sprintf(str,"CWD %s",*pp);
if(!ftp_cmd(csi,*lp,str,rsp))
return(0);
if(atoi(rsp)==250)
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_DIR:
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp)
return(0);
if(ftp_get(csi,*lp,*pp,NULL /* unused */, true /* DIR */)==true)
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_DELETE:
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp)
return(0);
sprintf(str,"DELE %s",*pp);
if(!ftp_cmd(csi,*lp,str,rsp))
return(0);
if(atoi(rsp)==250)
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_GET:
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp1 || !pp2)
return(0);
if(ftp_get(csi,*lp,*pp1,*pp2)==true)
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_PUT:
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp1 || !pp2)
return(0);
if(ftp_put(csi,*lp,*pp1,*pp2)==true)
csi->logic=LOGIC_TRUE;
return(0);
default:
errormsg(WHERE,ERR_CHK,"net sub-instruction",*(csi->ip-1));
return(0);
}
}
/* FTP Command/Response function */
bool sbbs_t::ftp_cmd(csi_t* csi, SOCKET sock, const char* cmdsrc, char* rsp)
{
char cmd[512];
int len;
time_t start;
if(cmdsrc!=NULL) {
sprintf(cmd,"%s\r\n",cmdsrc);
if(csi->ftp_mode&CS_FTP_ECHO_CMD)
bputs(cmd);
len=strlen(cmd);
if(sendsocket(sock,cmd,len)!=len) {
csi->socket_error=ERROR_VALUE;
return(FALSE);
}
}
if(rsp!=NULL) {
int rd;
char ch;
while(1) {
rd=0;
start=time(NULL);
while(rd<500) {
if(!online)
return(FALSE);
if(!socket_check(sock,&data_avail,NULL,1000))
if(!data_avail) {
if(time(NULL)-start>TIMEOUT_FTP_RESPONSE) {
lprintf(LOG_WARNING,"!ftp_cmd: TIMEOUT_FTP_RESPONSE (%d) exceeded"
,TIMEOUT_FTP_RESPONSE);
return(FALSE);
}
}
if(recv(sock, &ch, 1, 0)!=1) {
csi->socket_error=ERROR_VALUE;
return(FALSE);
}
if(ch=='\n' && rd>=1)
break;
rsp[rd++]=ch;
}
rsp[rd-1]=0;
if(csi->ftp_mode&CS_FTP_ECHO_RSP)
bprintf("%s\r\n",rsp);
if(rsp[0]!=' ' && rsp[3]!='-')
break;
}
}
return(TRUE);
}
SOCKET sbbs_t::ftp_data_sock(csi_t* csi, SOCKET ctrl_sock, SOCKADDR_IN* addr)
{
char cmd[512];
char rsp[512];
char* p;
socklen_t addr_len;
SOCKET data_sock;
int ip_b[4];
int port_b[2];
union {
DWORD dw;
BYTE b[sizeof(DWORD)];
} ip_addr;
union {
WORD w;
BYTE b[sizeof(WORD)];
} port;
if(csi->ftp_mode&CS_FTP_ASCII)
strcpy(cmd,"TYPE A");
else /* BINARY */
strcpy(cmd,"TYPE I");
if(!ftp_cmd(csi,ctrl_sock,cmd,rsp)
|| atoi(rsp)!=200) {
return(INVALID_SOCKET);
}
if((data_sock=open_socket(PF_INET, SOCK_STREAM, "ftp"))==INVALID_SOCKET) {
csi->socket_error=ERROR_VALUE;
return(INVALID_SOCKET);
}
memset(addr,0,sizeof(SOCKADDR_IN));
addr->sin_family = AF_INET;
if(bind(data_sock, (struct sockaddr *)addr,xp_sockaddr_len(addr))!= 0) {
csi->socket_error=ERROR_VALUE;
close_socket(data_sock);
return(INVALID_SOCKET);
}
if(csi->ftp_mode&CS_FTP_PASV) {
if(!ftp_cmd(csi,ctrl_sock,"PASV",rsp)
|| atoi(rsp)!=227 /* PASV response */) {
bprintf("ftp: failure, line %d",__LINE__);
close_socket(data_sock);
return(INVALID_SOCKET);
}
p=strchr(rsp,'(');
if(p==NULL) {
bprintf("ftp: failure, line %d",__LINE__);
close_socket(data_sock);
return(INVALID_SOCKET);
}
p++;
if(sscanf(p,"%u,%u,%u,%u,%u,%u"
,&ip_b[0],&ip_b[1],&ip_b[2],&ip_b[3]
,&port_b[0],&port_b[1])!=6) {
bprintf("ftp: failure, line %d",__LINE__);
close_socket(data_sock);
return(INVALID_SOCKET);
}
ip_addr.b[0]=ip_b[0]; ip_addr.b[1]=ip_b[1];
ip_addr.b[2]=ip_b[2]; ip_addr.b[3]=ip_b[3];
port.b[0]=port_b[0]; port.b[1]=port_b[1];
addr->sin_addr.s_addr=ip_addr.dw;
addr->sin_port=port.w;
} else { /* Normal (Active) FTP */
addr_len=sizeof(SOCKADDR_IN);
if(getsockname(data_sock, (struct sockaddr *)addr,&addr_len)!=0) {
csi->socket_error=ERROR_VALUE;
close_socket(data_sock);
return(INVALID_SOCKET);
}
SOCKADDR_IN ctrl_addr;
addr_len=sizeof(ctrl_addr);
if(getsockname(ctrl_sock, (struct sockaddr *)&ctrl_addr,&addr_len)!=0) {
csi->socket_error=ERROR_VALUE;
close_socket(data_sock);
return(INVALID_SOCKET);
}
if(listen(data_sock, 1)!= 0) {
csi->socket_error=ERROR_VALUE;
close_socket(data_sock);
return(INVALID_SOCKET);
}
ip_addr.dw=ntohl(ctrl_addr.sin_addr.s_addr);
port.w=ntohs(addr->sin_port);
sprintf(cmd,"PORT %u,%u,%u,%u,%u,%u"
,ip_addr.b[3]
,ip_addr.b[2]
,ip_addr.b[1]
,ip_addr.b[0]
,port.b[1]
,port.b[0]
);
if(!ftp_cmd(csi,ctrl_sock,cmd,rsp)
|| atoi(rsp)!=200 /* PORT response */) {
close_socket(data_sock);
return(INVALID_SOCKET);
}
}
return(data_sock);
}
bool sbbs_t::ftp_get(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest, bool dir)
{
char cmd[512];
char rsp[512];
char buf[4097];
int rd;
ulong total=0;
SOCKET data_sock;
socklen_t addr_len;
FILE* fp=NULL;
if((data_sock=ftp_data_sock(csi, ctrl_sock, &addr.in))==INVALID_SOCKET)
return(false);
if(csi->ftp_mode&CS_FTP_PASV) {
#if 0 // Debug
bprintf("Connecting to %s:%hd\r\n"
/* TODO: IPv6 */
if(connect(data_sock,&addr.addr,sizeof(SOCKADDR_IN))!=0) {
csi->socket_error=ERROR_VALUE;
close_socket(data_sock);
return(false);
}
}
if(dir)
sprintf(cmd,"LIST %s",src);
else
sprintf(cmd,"RETR %s",src);
if(!ftp_cmd(csi,ctrl_sock,cmd,rsp)
|| atoi(rsp)!=150 /* Open data connection */) {
close_socket(data_sock);
return(false);
}
if(!(csi->ftp_mode&CS_FTP_PASV)) { /* Normal (Active) FTP */
if(!socket_readable(data_sock, TIMEOUT_SOCK_LISTEN * 1000)) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
return(false);
}
SOCKET accept_sock;
addr_len=sizeof(addr);
==INVALID_SOCKET) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
return(false);
}
close_socket(data_sock);
data_sock=accept_sock;
}
if(!dir)
if((fp=fopen(dest,"wb"))==NULL) {
close_socket(data_sock);
return(false);
}
while(online) {
if(!socket_check(ctrl_sock,NULL,NULL,0))
break; /* Control connection lost */
if(!socket_check(data_sock,&data_avail,NULL,100))
break; /* Data connection lost */
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
if((rd=recv(data_sock, buf, sizeof(buf)-1, 0))<1)
break;
if(dir) {
buf[rd]=0;
bputs(buf);
} else
fwrite(buf,1,rd,fp);
total+=rd;
if(!dir && csi->ftp_mode&CS_FTP_HASH)
outchar('#');
}
if(!dir && csi->ftp_mode&CS_FTP_HASH) {
CRLF;
}
if(fp!=NULL)
fclose(fp);
close_socket(data_sock);
if(!ftp_cmd(csi,ctrl_sock,NULL,rsp)
|| atoi(rsp)!=226 /* Download complete */)
return(false);
bprintf("ftp: %lu bytes received.\r\n", total);
return(true);
}
bool sbbs_t::ftp_put(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest)
{
char cmd[512];
char rsp[512];
char path[MAX_PATH+1];
char buf[4097];
int rd;
ulong total=0;
SOCKET data_sock;
socklen_t addr_len;
FILE* fp=NULL;
bool error=false;
SAFECOPY(path,src);
if(!fexistcase(path))
if((data_sock=ftp_data_sock(csi, ctrl_sock, &addr.in))==INVALID_SOCKET) {
bprintf("ftp: failure, line %d",__LINE__);
return(false);
}
if(csi->ftp_mode&CS_FTP_PASV) {
#if 0 // Debug
bprintf("Connecting to %s:%hd\r\n"
bprintf("ftp: failure, line %d",__LINE__);
csi->socket_error=ERROR_VALUE;
close_socket(data_sock);
return(false);
}
}
if((fp=fopen(path,"rb"))==NULL) {
bprintf("ftp: failure, line %d",__LINE__);
close_socket(data_sock);
return(false);
}
sprintf(cmd,"STOR %s",dest);
if(!ftp_cmd(csi,ctrl_sock,cmd,rsp)
|| atoi(rsp)!=150 /* Open data connection */) {
bprintf("ftp: failure, line %d",__LINE__);
close_socket(data_sock);
fclose(fp);
return(false);
}
if(!(csi->ftp_mode&CS_FTP_PASV)) { /* Normal (Active) FTP */
if(!socket_readable(data_sock, TIMEOUT_SOCK_LISTEN * 1000)) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
fclose(fp);
SOCKET accept_sock;
addr_len=sizeof(addr);
==INVALID_SOCKET) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
fclose(fp);
return(false);
}
close_socket(data_sock);
data_sock=accept_sock;
}
while(online && !feof(fp)) {
rd=fread(buf,sizeof(char),sizeof(buf),fp);
if(rd<1) /* EOF or READ error */
break;
if(!socket_check(ctrl_sock,NULL,NULL,0))
break; /* Control connection lost */
if(sendsocket(data_sock,buf,rd)<1) {
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
error=true;
break;
}
total+=rd;
if(csi->ftp_mode&CS_FTP_HASH)
outchar('#');
}
if(csi->ftp_mode&CS_FTP_HASH) {
CRLF;
}
fclose(fp);
close_socket(data_sock);
if(!ftp_cmd(csi,ctrl_sock,NULL,rsp)
|| atoi(rsp)!=226 /* Upload complete */)
return(false);
if(!error)
bprintf("ftp: %lu bytes sent.\r\n", total);
return(!error);
}