Newer
Older
/* execnet.cpp */
/* Synchronet command shell/module TCP/IP Network functions */
/* $Id$ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2007 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 *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to mods@synchro.net *
* *
* 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;
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(SOCK_STREAM, NULL);
if(sock!=INVALID_SOCKET) {
SOCKADDR_IN addr;
memset(&addr,0,sizeof(addr));
addr.sin_addr.s_addr = htonl(startup->telnet_interface);
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;
if((i=recv(*lp,buf,w
,*(csi->ip-13)==CS_SOCKET_PEEK ? MSG_PEEK : 0))>0) {
csi->logic=LOGIC_TRUE;
buf[i]=0;
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, 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(SOCK_STREAM, "ftp"))==INVALID_SOCKET) {
csi->socket_error=ERROR_VALUE;
return(INVALID_SOCKET);
}
memset(addr,0,sizeof(SOCKADDR_IN));
addr->sin_addr.s_addr = htonl(startup->telnet_interface);
addr->sin_family = AF_INET;
if(bind(data_sock, (struct sockaddr *)addr,sizeof(SOCKADDR_IN))!= 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;
struct timeval tv;
fd_set socket_set;
if((data_sock=ftp_data_sock(csi, ctrl_sock, &addr))==INVALID_SOCKET)
return(false);
if(csi->ftp_mode&CS_FTP_PASV) {
#if 0 // Debug
bprintf("Connecting to %s:%hd\r\n"
,inet_ntoa(addr.sin_addr)
,ntohs(addr.sin_port));
#endif
if(connect(data_sock,(struct sockaddr *)&addr,sizeof(addr))!=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 */

rswindell
committed
tv.tv_sec=TIMEOUT_SOCK_LISTEN;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(data_sock,&socket_set);
result=select(data_sock+1,&socket_set,NULL,NULL,&tv);
if(result<1) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
return(false);
}
SOCKET accept_sock;
addr_len=sizeof(addr);
if((accept_sock=accept_socket(data_sock,(struct sockaddr*)&addr,&addr_len))
==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 */
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
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;
struct timeval tv;
fd_set socket_set;
SAFECOPY(path,src);
if(!fexistcase(path))
if((data_sock=ftp_data_sock(csi, ctrl_sock, &addr))==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"
,inet_ntoa(addr.sin_addr)
,ntohs(addr.sin_port));
#endif
if(connect(data_sock,(struct sockaddr *)&addr,sizeof(addr))!=0) {
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);
return(false);
}
if(!(csi->ftp_mode&CS_FTP_PASV)) { /* Normal (Active) FTP */

rswindell
committed
tv.tv_sec=TIMEOUT_SOCK_LISTEN;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(data_sock,&socket_set);
result=select(data_sock+1,&socket_set,NULL,NULL,&tv);
if(result<1) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
return(false);
}
SOCKET accept_sock;
addr_len=sizeof(addr);
if((accept_sock=accept_socket(data_sock,(struct sockaddr*)&addr,&addr_len))
==INVALID_SOCKET) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
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) {
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
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);
}