Added some core functions to the vnet FTP service and some fixes.

- FTP: added support for renaming file, create and remove directory.
- FTP: added FTP server status report and fixed SYST reply.
- bxhub fix: send all pending packets one after the other.
- TCP fix: when connecting, return default MSS option value 1460.
- TCP fix: handle the case when FIN is set in final data packet.
This commit is contained in:
Volker Ruppert 2020-05-30 14:44:53 +00:00
parent d540e5b040
commit f378ca5418
3 changed files with 127 additions and 15 deletions

View File

@ -55,6 +55,7 @@ typedef struct ftp_session {
unsigned data_xfer_size;
unsigned data_xfer_pos;
char *rel_path;
char *ren_old_name;
char dirlist_tmp[16];
struct ftp_session *next;
} ftp_session_t;
@ -666,7 +667,7 @@ void vnet_server_c::process_tcpipv4(Bit8u clientid, const Bit8u *ipheader,
unsigned ipheader_len, const Bit8u *l4pkt,
unsigned l4pkt_len)
{
unsigned tcphdr_len, tcpdata_len, tcprhdr_len, tcprdata_len = 0;
unsigned tcphdr_len, tcpdata_len, tcprhdr_len;
Bit32u tcp_seq_num, tcp_ack_num;
Bit16u tcp_src_port, tcp_dst_port, tcp_window;
Bit8u replybuf[MIN_RX_PACKET_LEN];
@ -725,7 +726,13 @@ void vnet_server_c::process_tcpipv4(Bit8u clientid, const Bit8u *ipheader,
tcprhdr->seq_num = htonl(1);
tcprhdr->ack_num = htonl(tcp_seq_num+1);
tcprhdr->window = htons(tcp_window);
tcprdata_len = 0;
// set MSS in reply to default 1460
Bit8u *opthdr = (Bit8u*)tcprhdr + tcprhdr_len;
opthdr[0] = 0x02;
opthdr[1] = 0x04;
opthdr[2] = 0x05;
opthdr[3] = 0xb4;
tcprhdr_len += 4;
tcp_conn = tcp_new_connection(clientid, tcp_src_port, tcp_dst_port);
tcp_conn->state = TCP_CONNECTING;
tcp_error = 0;
@ -738,14 +745,13 @@ void vnet_server_c::process_tcpipv4(Bit8u clientid, const Bit8u *ipheader,
}
tcp_remove_connection(tcp_conn);
return;
} else if (tcphdr->flags.fin) {
} else if (tcphdr->flags.fin && (tcpdata_len == 0)) {
if (tcp_conn->state == TCP_CONNECTED) {
tcprhdr->flags.fin = 1;
tcprhdr->flags.ack = 1;
tcprhdr->seq_num = htonl(tcp_conn->host_seq_num);
tcprhdr->ack_num = htonl(tcp_seq_num+1);
tcprhdr->window = htons(tcp_window);
tcprdata_len = 0;
tcp_conn->state = TCP_DISCONNECTING;
tcp_error = 0;
} else if (tcp_conn->state == TCP_DISCONNECTING) {
@ -775,6 +781,10 @@ void vnet_server_c::process_tcpipv4(Bit8u clientid, const Bit8u *ipheader,
}
(*func)((void *)this, tcp_conn, tcp_data, tcpdata_len);
}
if (tcphdr->flags.fin) {
tcp_conn->guest_seq_num++;
tcpipv4_send_fin(tcp_conn, 0);
}
}
return;
} else {
@ -790,11 +800,10 @@ void vnet_server_c::process_tcpipv4(Bit8u clientid, const Bit8u *ipheader,
tcprhdr->seq_num = htonl(1);
tcprhdr->ack_num = htonl(tcp_seq_num+1);
tcprhdr->window = 0;
tcprdata_len = 0;
BX_ERROR(("tcp - port %u unhandled or in use", tcp_dst_port));
}
host_to_guest_tcpipv4(clientid, tcp_dst_port, tcp_src_port, replybuf,
tcprdata_len, tcprhdr_len);
host_to_guest_tcpipv4(clientid, tcp_dst_port, tcp_src_port, replybuf, 0,
tcprhdr_len);
}
void vnet_server_c::host_to_guest_tcpipv4(Bit8u clientid, Bit16u src_port,
@ -866,7 +875,7 @@ unsigned vnet_server_c::tcpipv4_send_data(tcp_conn_t *tcp_conn, const Bit8u *dat
pos += sendsize;
} while (pos < data_len);
} else {
tcpipv4_send_fin(tcp_conn);
tcpipv4_send_fin(tcp_conn, 1);
}
return pos;
}
@ -887,7 +896,7 @@ void vnet_server_c::tcpipv4_send_ack(tcp_conn_t *tcp_conn, unsigned data_len)
replybuf, 0, tcphdr_len);
}
void vnet_server_c::tcpipv4_send_fin(tcp_conn_t *tcp_conn)
void vnet_server_c::tcpipv4_send_fin(tcp_conn_t *tcp_conn, bx_bool host_fin)
{
Bit8u replybuf[MIN_RX_PACKET_LEN];
tcp_header_t *tcphdr = (tcp_header_t *)&replybuf[34];
@ -900,7 +909,7 @@ void vnet_server_c::tcpipv4_send_fin(tcp_conn_t *tcp_conn)
tcphdr->ack_num = htonl(tcp_conn->guest_seq_num);
tcphdr->window = htons(tcp_conn->window);
tcp_conn->state = TCP_DISCONNECTING;
tcp_conn->host_port_fin = 1;
tcp_conn->host_port_fin = host_fin;
host_to_guest_tcpipv4(tcp_conn->clientid, tcp_conn->dst_port, tcp_conn->src_port,
replybuf, 0, tcphdr_len);
}
@ -1033,6 +1042,24 @@ bx_bool ftp_file_exists(const char *fname, unsigned *size)
return exists;
}
bx_bool ftp_mkdir(const char *path)
{
#ifndef WIN32
return (mkdir(path, 0755) == 0);
#else
return (CreateDirectory(path, NULL) != 0);
#endif
}
bx_bool ftp_rmdir(const char *path)
{
#ifndef WIN32
return (rmdir(path) == 0);
#else
return (RemoveDirectory(path) != 0);
#endif
}
// FTP service handler
void vnet_server_c::tcpipv4_ftp_handler(void *this_ptr, tcp_conn_t *tcp_conn, const Bit8u *data, unsigned data_len)
@ -1107,7 +1134,7 @@ void vnet_server_c::tcpipv4_ftp_handler_ns(tcp_conn_t *tcp_conn, const Bit8u *da
if (fs->data_xfer_fd >= 0) {
close(fs->data_xfer_fd);
fs->data_xfer_fd = -1;
tcpipv4_send_fin(tcpc_data);
tcpipv4_send_fin(tcpc_data, 1);
ftp_send_reply(tcpc_cmd, "426 Transfer aborted.");
ftp_send_reply(tcpc_cmd, "226 Transfer abort complete.");
} else {
@ -1169,6 +1196,22 @@ void vnet_server_c::tcpipv4_ftp_handler_ns(tcp_conn_t *tcp_conn, const Bit8u *da
if (tcpc_data != NULL) {
ftp_list_directory(tcpc_cmd, tcpc_data);
}
} else if (!stricmp(cmd, "MKD")) {
if (!fs->anonymous) {
sprintf(tmp_path, "%s/%s", fs->rel_path, arg);
if (!ftp_directory_exists(tftp_root, tmp_path)) {
sprintf(tmp_path, "%s%s/%s", tftp_root, fs->rel_path, arg);
if (ftp_mkdir(tmp_path)) {
ftp_send_reply(tcpc_cmd, "257 New directory created.");
} else {
ftp_send_reply(tcpc_cmd, "550 Directory creation failed.");
}
} else {
ftp_send_reply(tcpc_cmd, "550 Directory already exists.");
}
} else {
ftp_send_reply(tcpc_cmd, "550 MKD operation not permitted.");
}
} else if (!stricmp(cmd, "NOOP")) {
ftp_send_reply(tcpc_cmd, "200 Command OK.");
} else if (!stricmp(cmd, "OPTS")) {
@ -1201,8 +1244,56 @@ void vnet_server_c::tcpipv4_ftp_handler_ns(tcp_conn_t *tcp_conn, const Bit8u *da
if (tcpc_data != NULL) {
ftp_send_file(tcpc_cmd, tcpc_data, arg);
}
} else if (!stricmp(cmd, "RMD")) {
if (!fs->anonymous) {
sprintf(tmp_path, "%s/%s", fs->rel_path, arg);
if (ftp_directory_exists(tftp_root, tmp_path)) {
sprintf(tmp_path, "%s%s/%s", tftp_root, fs->rel_path, arg);
if (ftp_rmdir(tmp_path)) {
ftp_send_reply(tcpc_cmd, "250 RMD operation successful.");
} else {
ftp_send_reply(tcpc_cmd, "550 RMD operation failed.");
}
} else {
ftp_send_reply(tcpc_cmd, "550 Directory not found.");
}
} else {
ftp_send_reply(tcpc_cmd, "550 RMD operation not permitted.");
}
} else if (!stricmp(cmd, "RNFR")) {
if (!fs->anonymous) {
sprintf(tmp_path, "%s%s/%s", tftp_root, fs->rel_path, arg);
if (ftp_file_exists(tmp_path, NULL)) {
fs->ren_old_name = new char[strlen(tmp_path)+1];
strcpy(fs->ren_old_name, tmp_path);
ftp_send_reply(tcpc_cmd, "350 File exists. Ready for new name.");
} else {
ftp_send_reply(tcpc_cmd, "550 File not found.");
}
} else {
ftp_send_reply(tcpc_cmd, "550 Rename operation not permitted.");
}
} else if (!stricmp(cmd, "RNTO")) {
if (!fs->anonymous && (fs->ren_old_name != NULL)) {
sprintf(tmp_path, "%s%s/%s", tftp_root, fs->rel_path, arg);
if (!ftp_file_exists(tmp_path, NULL)) {
if (rename(fs->ren_old_name, tmp_path) == 0) {
ftp_send_reply(tcpc_cmd, "250 File renamed successfully.");
} else {
ftp_send_reply(tcpc_cmd, "550 Rename operation failed.");
}
} else {
ftp_send_reply(tcpc_cmd, "550 File exists.");
}
delete [] fs->ren_old_name;
fs->ren_old_name = NULL;
} else {
ftp_send_reply(tcpc_cmd, "550 Rename operation not permitted.");
}
} else if (!stricmp(cmd, "SIZE")) {
ftp_get_filesize(tcpc_cmd, arg);
} else if (!stricmp(cmd, "STAT")) {
ftp_send_status(tcpc_cmd);
} else if (!stricmp(cmd, "STOR")) {
if (tcpc_data != NULL) {
if (!fs->anonymous) {
@ -1212,7 +1303,7 @@ void vnet_server_c::tcpipv4_ftp_handler_ns(tcp_conn_t *tcp_conn, const Bit8u *da
}
}
} else if (!stricmp(cmd, "SYST")) {
ftp_send_reply(tcpc_cmd, "215 UNIX Type: Bochs Version: 2.6.11");
ftp_send_reply(tcpc_cmd, "215 UNIX Type: L8 Version: Bochs 2.6.11");
} else if (!stricmp(cmd, "TYPE")) {
if (!stricmp(arg, "A") || !stricmp(arg, "I")) {
sprintf(reply, "200 Type set to %s.", arg);
@ -1260,7 +1351,7 @@ void vnet_server_c::tcpipv4_ftp_handler_ns(tcp_conn_t *tcp_conn, const Bit8u *da
if (fs->data_xfer_fd >= 0) {
ftp_send_data(tcpc_cmd, tcpc_data);
} else {
tcpipv4_send_fin(tcpc_data);
tcpipv4_send_fin(tcpc_data, 1);
}
}
}
@ -1277,6 +1368,26 @@ void vnet_server_c::ftp_send_reply(tcp_conn_t *tcp_conn, const char *msg)
}
}
void vnet_server_c::ftp_send_status(tcp_conn_t *tcp_conn)
{
char reply[256], linebuf[80];
Bit8u *ipv4addr = client[tcp_conn->clientid].ipv4addr;
sprintf(reply, "211- Bochs FTP server status:%c%c", 13, 10);
sprintf(linebuf, " Connected to %u.%u.%u.%u%c%c", ipv4addr[0], ipv4addr[1],
ipv4addr[2], ipv4addr[3], 13, 10);
strcat(reply, linebuf);
sprintf(linebuf, " Logged in as ftpuser%c%c", 13, 10);
strcat(reply, linebuf);
sprintf(linebuf, " Type: ASCII, Form: Nonprint; STRUcture: File; Transfer MODE: Stream%c%c", 13, 10);
strcat(reply, linebuf);
sprintf(linebuf, " No data connection%c%c", 13, 10);
strcat(reply, linebuf);
sprintf(linebuf, "211 End of status%c%c", 13, 10);
strcat(reply, linebuf);
tcpipv4_send_data(tcp_conn, (Bit8u*)reply, strlen(reply), 1);
}
void vnet_server_c::ftp_send_data_prep(tcp_conn_t *tcpc_cmd, tcp_conn_t *tcpc_data,
const char *path, unsigned data_len)
{

View File

@ -275,13 +275,14 @@ private:
unsigned tcpipv4_send_data(tcp_conn_t *tcp_conn, const Bit8u *data,
unsigned data_len, bx_bool push);
void tcpipv4_send_ack(tcp_conn_t *tcp_conn, unsigned data_len);
void tcpipv4_send_fin(tcp_conn_t *tcp_conn);
void tcpipv4_send_fin(tcp_conn_t *tcp_conn, bx_bool host_fin);
static void tcpipv4_ftp_handler(void *this_ptr, tcp_conn_t *tcp_conn,
const Bit8u *data, unsigned data_len);
void tcpipv4_ftp_handler_ns(tcp_conn_t *tcp_conn, const Bit8u *data,
unsigned data_len);
void ftp_send_reply(tcp_conn_t *tcp_conn, const char *msg);
void ftp_send_status(tcp_conn_t *tcp_conn);
void ftp_send_data_prep(tcp_conn_t *tcpc_cmd, tcp_conn_t *tcpc_data,
const char *path, unsigned data_len);
void ftp_send_data(tcp_conn_t *tcpc_cmd, tcp_conn_t *tcpc_data);

View File

@ -362,7 +362,7 @@ int CDECL main(int argc, char **argv)
}
}
// send reply from builtin service
if (hclient[i].pending_reply_size > 0) {
while (hclient[i].pending_reply_size > 0) {
send_packet(&hclient[i], hclient[i].reply_buffer, hclient[i].pending_reply_size);
// check for another pending packet
hclient[i].pending_reply_size = vnet_server.get_packet(hclient[i].reply_buffer);