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 */
#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;
ushort w;
int i;
bool rd;
int32_t *lp, *lp1, *lp2;
time_t start;
switch (*(csi->ip++)) { /* sub-op-code stored as next byte */
lp = getintvar(csi, *(int32_t *)csi->ip);
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (csi->sockets >= MAX_SOCKETS)
SOCKET sock = open_socket(PF_INET, SOCK_STREAM, NULL);
if (sock != INVALID_SOCKET) {
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
if ((i = bind(sock, (struct sockaddr *) &addr, sizeof (addr))) != 0) {
csi->socket_error = SOCKET_ERRNO;
for (i = 0; i < csi->sockets; i++)
if (!csi->socket[i])
csi->socket[i] = *lp;
if (i == csi->sockets)
csi->logic = LOGIC_TRUE;
}
lp = getintvar(csi, *(int32_t *)csi->ip);
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (lp && *lp) {
csi->logic = close_socket((SOCKET)*lp);
csi->socket_error = SOCKET_ERRNO;
for (i = 0; i < csi->sockets; i++)
if (csi->socket[i] == (SOCKET)*lp)
csi->socket[i] = 0;
*lp = 0;
lp = getintvar(csi, *(int32_t *)csi->ip);
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (lp == NULL || *lp == INVALID_SOCKET)
if (socket_check(*lp, NULL, NULL, 0) == TRUE)
csi->logic = LOGIC_TRUE;
csi->socket_error = SOCKET_ERRNO;
case CS_SOCKET_CONNECT:
lp = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
pp = getstrvar(csi, *(int32_t *)csi->ip); /* address */
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)
if ((ip_addr = resolve_ip(*pp)) == INADDR_NONE)
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 = SOCKET_ERRNO;
csi->logic = LOGIC_TRUE;
lp1 = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
csi->socket_error = 0;
lp1 = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
lp2 = getintvar(csi, *(int32_t *)csi->ip); /* var */
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (ioctlsocket(*lp1, FIONREAD, (ulong*)lp2) == 0)
csi->logic = LOGIC_TRUE;
csi->socket_error = SOCKET_ERRNO;
case CS_SOCKET_PEEK:
case CS_SOCKET_READ:
lp = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
pp = getstrvar(csi, *(int32_t *)csi->ip); /* buffer */
csi->ip += 4;
w = *(ushort *)csi->ip; /* length */
csi->ip += 2;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (w < 1 || w > sizeof(buf) - 1)
w = sizeof(buf) - 1;
if ((rcv_count = recv(*lp, buf, w
, *(csi->ip - 13) == CS_SOCKET_PEEK ? MSG_PEEK : 0)) > 0) {
csi->logic = LOGIC_TRUE;
buf[rcv_count] = 0;
if (csi->etx) {
p = strchr(buf, csi->etx);
*pp = copystrvar(csi, *pp, buf);
csi->socket_error = SOCKET_ERRNO;
lp = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
pp = getstrvar(csi, *(int32_t *)csi->ip); /* buffer */
csi->ip += 4;
w = *(ushort *)csi->ip; /* length */
csi->ip += 2;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (w < 1 || w > sizeof(buf) - 1)
w = sizeof(buf) - 1;
start = time(NULL);
for (i = 0; i < w;) {
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);
}
}
if (recv(*lp, &ch, 1, 0) != 1) {
csi->socket_error = SOCKET_ERRNO;
if (ch == '\n' && i >= 1)
if (i > 0 && buf[i - 1] == '\r')
buf[i - 1] = 0;
else
if (csi->etx) {
p = strchr(buf, csi->etx);
*pp = copystrvar(csi, *pp, buf);
csi->logic = LOGIC_TRUE;
case CS_SOCKET_WRITE:
lp = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
pp = getstrvar(csi, *(int32_t *)csi->ip); /* buffer */
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (!lp || !pp || !(*pp))
if (sendsocket(*lp, *pp, strlen(*pp)) > 0)
csi->logic = LOGIC_TRUE;
csi->socket_error = SOCKET_ERRNO;
lp = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
pp1 = getstrvar(csi, *(int32_t *)csi->ip); /* username */
csi->ip += 4;
pp2 = getstrvar(csi, *(int32_t *)csi->ip); /* password */
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (!lp || !pp1 || !pp2)
if (!ftp_cmd(csi, *lp, NULL, rsp))
snprintf(str, sizeof str, "USER %s", *pp1);
if (!ftp_cmd(csi, *lp, str, rsp))
if (atoi(rsp) == 331) { /* Password needed */
snprintf(str, sizeof str, "PASS %s", *pp2);
if (!ftp_cmd(csi, *lp, str, rsp))
if (atoi(rsp) == 230) /* Login successful */
csi->logic = LOGIC_TRUE;
case CS_FTP_LOGOUT:
lp = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (!ftp_cmd(csi, *lp, "QUIT", rsp))
if (atoi(rsp) == 221) /* Logout successful */
csi->logic = LOGIC_TRUE;
lp = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (!lp)
if (!ftp_cmd(csi, *lp, "PWD", rsp))
if (atoi(rsp) == 257) /* pathname */
csi->logic = LOGIC_TRUE;
lp = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
pp = getstrvar(csi, *(int32_t *)csi->ip); /* path */
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (!lp || !pp)
snprintf(str, sizeof str, "CWD %s", *pp);
if (!ftp_cmd(csi, *lp, str, rsp))
if (atoi(rsp) == 250)
csi->logic = LOGIC_TRUE;
lp = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
pp = getstrvar(csi, *(int32_t *)csi->ip); /* path */
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (ftp_get(csi, *lp, *pp, NULL /* unused */, true /* DIR */) == true)
csi->logic = LOGIC_TRUE;
case CS_FTP_DELETE:
lp = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
pp = getstrvar(csi, *(int32_t *)csi->ip); /* path */
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (!lp || !pp)
snprintf(str, sizeof str, "DELE %s", *pp);
if (!ftp_cmd(csi, *lp, str, rsp))
if (atoi(rsp) == 250)
csi->logic = LOGIC_TRUE;
case CS_FTP_GET:
lp = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
pp1 = getstrvar(csi, *(int32_t *)csi->ip); /* src path */
csi->ip += 4;
pp2 = getstrvar(csi, *(int32_t *)csi->ip); /* dest path */
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (!lp || !pp1 || !pp2)
if (ftp_get(csi, *lp, *pp1, *pp2) == true)
csi->logic = LOGIC_TRUE;
lp = getintvar(csi, *(int32_t *)csi->ip); /* socket */
csi->ip += 4;
pp1 = getstrvar(csi, *(int32_t *)csi->ip); /* src path */
csi->ip += 4;
pp2 = getstrvar(csi, *(int32_t *)csi->ip); /* dest path */
csi->ip += 4;
csi->logic = LOGIC_FALSE;
csi->socket_error = 0;
if (!lp || !pp1 || !pp2)
if (ftp_put(csi, *lp, *pp1, *pp2) == true)
csi->logic = LOGIC_TRUE;
errormsg(WHERE, ERR_CHK, "net sub-instruction", *(csi->ip - 1));
}
}
/* FTP Command/Response function */
bool sbbs_t::ftp_cmd(csi_t* csi, SOCKET sock, const char* cmdsrc, char* rsp)
char cmd[512];
int len;
bool data_avail;
time_t start;
if (cmdsrc != NULL) {
snprintf(cmd, sizeof cmd, "%s\r\n", cmdsrc);
if (csi->ftp_mode & CS_FTP_ECHO_CMD)
len = strlen(cmd);
if (sendsocket(sock, cmd, len) != len) {
csi->socket_error = SOCKET_ERRNO;
while (1) {
rd = 0;
start = time(NULL);
while (rd < 500) {
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);
}
}
if (recv(sock, &ch, 1, 0) != 1) {
csi->socket_error = SOCKET_ERRNO;
if (ch == '\n' && rd >= 1)
rsp[rd - 1] = 0;
if (csi->ftp_mode & CS_FTP_ECHO_RSP)
bprintf("%s\r\n", rsp);
if (rsp[0] != ' ' && rsp[3] != '-')
}
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];
DWORD dw;
BYTE b[sizeof(DWORD)];
WORD w;
BYTE b[sizeof(WORD)];
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 = SOCKET_ERRNO;
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 = SOCKET_ERRNO;
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;
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 = SOCKET_ERRNO;
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 = SOCKET_ERRNO;
close_socket(data_sock);
return INVALID_SOCKET;
if (listen(data_sock, 1) != 0) {
csi->socket_error = SOCKET_ERRNO;
close_socket(data_sock);
return INVALID_SOCKET;
ip_addr.dw = ntohl(ctrl_addr.sin_addr.s_addr);
port.w = ntohs(addr->sin_port);
snprintf(cmd, sizeof 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;
}
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;
bool data_avail;
ulong total = 0;
SOCKET data_sock;
union xp_sockaddr addr;
socklen_t addr_len;
FILE* fp = NULL;
if ((data_sock = ftp_data_sock(csi, ctrl_sock, &addr.in)) == INVALID_SOCKET)
if (csi->ftp_mode & CS_FTP_PASV) {
bprintf("Connecting to %s:%hd\r\n"
, inet_ntoa(addr.in.sin_addr)
, ntohs(addr.in.sin_port));
if (connect(data_sock, &addr.addr, sizeof(SOCKADDR_IN)) != 0) {
csi->socket_error = SOCKET_ERRNO;
close_socket(data_sock);
}
if (dir)
snprintf(cmd, sizeof cmd, "LIST %s", src);
else
snprintf(cmd, sizeof cmd, "RETR %s", src);
if (!ftp_cmd(csi, ctrl_sock, cmd, rsp)
|| atoi(rsp) != 150 /* Open data connection */) {
close_socket(data_sock);
}
if (!(csi->ftp_mode & CS_FTP_PASV)) { /* Normal (Active) FTP */
if (!socket_readable(data_sock, TIMEOUT_SOCK_LISTEN * 1000)) {
csi->socket_error = SOCKET_ERRNO;
SOCKET accept_sock;
addr_len = sizeof(addr);
if ((accept_sock = accept_socket(data_sock, &addr, &addr_len))
== INVALID_SOCKET) {
csi->socket_error = SOCKET_ERRNO;
closesocket(data_sock);
}
close_socket(data_sock);
data_sock = accept_sock;
if (!dir)
if ((fp = fopen(dest, "wb")) == NULL) {
close_socket(data_sock);
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 */
if ((rd = recv(data_sock, buf, sizeof(buf) - 1, 0)) < 1)
if (dir) {
buf[rd] = 0;
fwrite(buf, 1, rd, fp);
total += rd;
if (!dir && csi->ftp_mode & CS_FTP_HASH)
outchar('#');
}
if (!dir && csi->ftp_mode & CS_FTP_HASH) {
fclose(fp);
close_socket(data_sock);
if (!ftp_cmd(csi, ctrl_sock, NULL, rsp)
|| atoi(rsp) != 226 /* Download complete */)
bprintf("ftp: %lu bytes received.\r\n", total);
}
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;
union xp_sockaddr addr;
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__);
if (csi->ftp_mode & CS_FTP_PASV) {
bprintf("Connecting to %s:%hd\r\n"
, inet_ntoa(addr.in.sin_addr)
, ntohs(addr.in.sin_port));
if (connect(data_sock, &addr.addr, sizeof(addr.in)) != 0) {
bprintf("ftp: failure, line %d", __LINE__);
csi->socket_error = SOCKET_ERRNO;
close_socket(data_sock);
}
if ((fp = fopen(path, "rb")) == NULL) {
bprintf("ftp: failure, line %d", __LINE__);
close_socket(data_sock);
}
snprintf(cmd, sizeof 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);
}
if (!(csi->ftp_mode & CS_FTP_PASV)) { /* Normal (Active) FTP */
if (!socket_readable(data_sock, TIMEOUT_SOCK_LISTEN * 1000)) {
csi->socket_error = SOCKET_ERRNO;
fclose(fp);
SOCKET accept_sock;
addr_len = sizeof(addr);
if ((accept_sock = accept_socket(data_sock, &addr, &addr_len))
== INVALID_SOCKET) {
csi->socket_error = SOCKET_ERRNO;
closesocket(data_sock);
fclose(fp);
}
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 */
if (!socket_check(ctrl_sock, NULL, NULL, 0))
break; /* Control connection lost */
if (sendsocket(data_sock, buf, rd) < 1) {
error = true;
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 */)
bprintf("ftp: %lu bytes sent.\r\n", total);