ponyprog/socksrv.cpp
Claudio Lanconelli 5305b1ae46 Initial revision
2000-02-05 22:37:44 +00:00

618 lines
14 KiB
C++

//=========================================================================//
//-------------------------------------------------------------------------//
// socksrv.cpp -- SocketServer class //
// This file is part of PonyProg. //
//-------------------------------------------------------------------------//
// //
// PonyProg - Serial Device Programmer //
// //
// Copyright (C) 1997-1999 Claudio Lanconelli //
// //
// e-mail: lanconel@cs.unibo.it //
// http://www.cs.unibo.it/~lanconel //
// //
//-------------------------------------------------------------------------//
// //
// 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 version2 of //
// the License, or (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
// General Public License for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program (see COPYING); if not, write to the //
// Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //
// //
//-------------------------------------------------------------------------//
//=========================================================================//
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include "socksrv.h"
#ifdef _UDP_SERVER
#include "e2app.h"
#include "e2awinfo.h"
#endif
#define PONYID "Pony"
#ifndef OK
#define OK 0
#endif
#ifdef _UDP_SERVER
SocketServer::SocketServer(e2CmdWindow *cwin, int port)
#else
SocketServer::SocketServer(int port)
#endif
{
cmdWin = cwin;
error_code = OK;
error_msg[0] = '\0';
tcp_port = (port > 1024) ? port : DEF_PORT;
udp_port = tcp_port + 1;
udp_sockfd = INVALID_SOCKET;
tcp_sockfd = INVALID_SOCKET;
/*
* Initialize Sockets
*/
if ( !SocketInit()
#ifdef _UDP_SERVER
|| !cmdWin
#endif
)
{
SetError(1, "error in Socket initialization");
}
}
SocketServer::~SocketServer()
{
if (udp_sockfd != INVALID_SOCKET)
{
closesocket(udp_sockfd);
udp_sockfd = INVALID_SOCKET;
}
if (tcp_sockfd != INVALID_SOCKET)
{
closesocket(tcp_sockfd);
tcp_sockfd = INVALID_SOCKET;
}
SocketEnd();
}
void SocketServer::SetError(int n, char *str)
{
error_code = n;
if (str)
strncpy(error_msg, str, sizeof(error_msg));
error_msg[sizeof(error_msg) - 1] = '\0';
}
int SocketServer::GetError(char *str, int len)
{
if (str != 0 && len > 0)
{
strncpy(str, error_msg, len);
}
return error_code;
}
#ifdef _UDP_CLIENT
int SocketServer::UDPSendCommand(char *ip_address, int &command, char *param, int paramsize)
{
//Check parameters
if ( ip_address == 0 || paramsize <= 0 || param == 0 ||
command <= ACKNOWLEDGE || command >= LAST_ACTION )
{
SetError(-2, "client: bad parameters");
return -2;
}
//Build the message
strcpy(msg.Id, PONYID);
msg.Action = command;
strncpy(msg.msg, param, MAXMESG-1);
//Prepare socket
if (udp_sockfd != INVALID_SOCKET)
{
closesocket(udp_sockfd);
udp_sockfd = INVALID_SOCKET;
}
/*
* Fill in the "serv_addr" field with the server address
*/
struct sockaddr_in serv_addr, cli_addr;
struct hostent *hostptr;
unsigned int inaddr;
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(ip_port);
if ((inaddr = inet_addr(ip_address)) != INADDR_NONE)
{
serv_addr.sin_addr.s_addr = inaddr;
}
else
if ((hostptr = gethostbyname(ip_address)) != NULL)
{
serv_addr.sin_addr = *((struct in_addr*)hostptr->h_addr);
}
else
{
SetError(1, "client: bad server address");
return 1;
}
/*
* Open the UDP socket
*/
udp_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_sockfd == INVALID_SOCKET)
{
#ifdef WIN32
errno = WSAGetLastError();
SetError(errno, GetSockErrorString(errno));
#else
SetError(2, "client: can't open datagram socket");
#endif
return 2;
}
/*
* Request for a local address
*/
bzero((char *)&cli_addr, sizeof(cli_addr));
cli_addr.sin_family = AF_INET;
cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
cli_addr.sin_port = htons(0);
if (bind(udp_sockfd, (struct sockaddr *)&cli_addr, sizeof(cli_addr)) < 0)
{
SetError(3, "client: can't bind local address");
return 3;
}
/*
* Send the service request to the server
*/
CalcSum();
int n = sizeof(msg);
if (sendto(udp_sockfd, (char *)&msg, n, 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != n)
{
SetError(5, "clinet: sendto error");
return 5;
}
//Wait for reply
fd_set rfds;
struct timeval tv;
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(udp_sockfd, &rfds);
// Wait 10 seconds for the ack
tv.tv_sec = 10;
tv.tv_usec = 0;
n = select(udp_sockfd+1, &rfds, NULL, NULL, &tv);
if (n < 0) //Error
{
SetError(7, "client: select() error");
return 7;
}
else
if (n == 0) //Timeout
{
SetError(8, "client: no response from server (timeout)");
return 8;
}
//Read reply
socklen_t fromlen = 0;
n = recvfrom(udp_sockfd, (char *)&msg, sizeof(msg), 0, NULL, &fromlen);
if (n < 0)
{
SetError(4, "client: recvfrom() error");
return 4;
}
if (!CheckSum())
{
SetError(10, "Bad checksum\n");
return 10;
}
if (msg.Action != ACKNOWLEDGE)
{
SetError(9, "client: bad acknowledge");
return 9;
}
//Wait for result
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(udp_sockfd, &rfds);
// Wait 10 minutes for the result
tv.tv_sec = 60*10;
tv.tv_usec = 0;
n = select(udp_sockfd+1, &rfds, NULL, NULL, &tv);
if (n < 0) //Error
{
SetError(7, "client: select() error");
return 7;
}
else
if (n == 0) //Timeout
{
SetError(11, "client: no response from server (timeout)");
return 11;
}
//Read result
fromlen = 0;
n = recvfrom(udp_sockfd, (char *)&msg, sizeof(msg), 0, NULL, &fromlen);
if (n < 0)
{
SetError(4, "client: recvfrom() error");
return 4;
}
if (!CheckSum())
{
SetError(10, "Bad checksum\n");
return 10;
}
//Return result
command = msg.Action;
strncpy(param, msg.msg, paramsize-1);
param[paramsize-1] = '\0';
if (udp_sockfd != INVALID_SOCKET)
{
closesocket(udp_sockfd);
udp_sockfd = INVALID_SOCKET;
}
return OK;
}
int SocketServer::TCPSend(void *buffer, int size)
{
return OK;
}
#endif
#ifdef _UDP_SERVER
int SocketServer::TCPReceive(void *buffer, int &size)
{
return OK;
}
int SocketServer::UDPMainLoop()
{
struct sockaddr_in serv_addr, cli_addr;
if (udp_sockfd != INVALID_SOCKET)
{
closesocket(udp_sockfd);
udp_sockfd = INVALID_SOCKET;
}
/*
* Open the UDP socket
*/
udp_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_sockfd == INVALID_SOCKET)
{
#ifdef WIN32
errno = WSAGetLastError();
SetError(errno, GetSockErrorString(errno));
#else
SetError(2, "server: can't open datagram socket");
#endif
return 2;
}
/*
* Bind to the local address to receive requests
*/
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(udp_port);
if (bind(udp_sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
SetError(3, "server: can't bind local address");
return 3;
}
int progress = 0;
while ( !THEAPP->GetAbortFlag() )
{
if (++progress > 200)
progress = 1;
if ( (progress & 1) )
THEAPP->SetProgress(progress >> 1);
fd_set rfds;
struct timeval tv;
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(udp_sockfd, &rfds);
/* Wait half a second */
tv.tv_sec = 0;
tv.tv_usec = 500000;
int n = select(udp_sockfd+1, &rfds, NULL, NULL, &tv);
/* Don't rely on the value of tv now! */
if (n < 0) //Error
{
SetError(7, "server: select() error");
return 7;
}
else
if (n == 0) //Timeout
{
continue;
}
/*
* Wait for the request from clients
*/
socklen_t clilen = sizeof(cli_addr);
n = recvfrom(udp_sockfd, (char *)&msg, sizeof(msg), 0, (struct sockaddr *)&cli_addr, &clilen);
if (n != sizeof(msg))
{
#ifdef WIN32
if (n < 0)
{
errno = WSAGetLastError();
SetError(errno, GetSockErrorString(errno));
}
else
#endif
SetError(4, "server: recvfrom error");
return 4;
}
//int err = 0;
/*
* Check the request
*/
if (strcmp(msg.Id, PONYID) != 0)
{
strcpy(msg.Id, PONYID);
msg.Action = -1;
strcpy(msg.msg, "Error: bad request\n");
}
else
{
//Check for parameters
if ( !CheckSum() )
{
strcpy(msg.msg, "Error: bad checksum\n");
msg.Action = -3;
}
else
{
if (msg.Action > ACKNOWLEDGE && msg.Action < LAST_ACTION)
{
int action = msg.Action;
/*
* Send the acknowledge to the client
*/
msg.Action = ACKNOWLEDGE;
msg.msg[0] = '\0';
CalcSum();
if (sendto(udp_sockfd, (char *)&msg, sizeof(msg), 0, (struct sockaddr *)&cli_addr, clilen) != sizeof(msg))
{
SetError(5, "server: sendto error");
return 5;
}
switch(action)
{
case ACTION_READ:
msg.Action = cmdWin->CmdRead();
break;
case ACTION_WRITE:
msg.Action = cmdWin->CmdWrite();
break;
case ACTION_VERIFY:
msg.Action = cmdWin->CmdVerify();
break;
case ACTION_RESET:
msg.Action = cmdWin->CmdReset();
break;
case ACTION_LOAD:
break;
case ACTION_RELOAD:
msg.Action = cmdWin->CmdReload();
break;
case ACTION_SAVE:
msg.Action = cmdWin->CmdSave();
break;
case ACTION_SAVEAS:
break;
case ACTION_LOCK:
break;
case ACTION_DOWNLOAD:
{
e2AppWinInfo *aptr = cmdWin->GetAWInfo();
if (aptr)
{
int bufsize = aptr->GetBufSize();
msg.Action = TCPReceive(aptr->GetBufPtr(), bufsize);
}
}
break;
} // switch
} //if
else
{
strcpy(msg.msg, "Error: bad parameters\n");
msg.Action = -2;
}
} //else
}
/*
* Send the result back to the client
*/
CalcSum();
if (sendto(udp_sockfd, (char *)&msg, sizeof(msg), 0, (struct sockaddr *)&cli_addr, clilen) != sizeof(msg))
{
SetError(5, "server: sendto error");
return 5;
}
}
return OK;
}
#endif
void SocketServer::CalcSum()
{
msg.checksum = 0;
BYTE *bp = (BYTE *) &msg;
int ck = 0;
unsigned int k;
for (k = 0; k < sizeof(PonyMessage); k++)
{
ck += bp[k];
}
ck &= 0xFF;
msg.checksum = (unsigned char)(0x100 - ck);
}
int SocketServer::CheckSum()
{
BYTE *bp = (BYTE *) &msg;
unsigned char ck = 0;
unsigned int k;
for (k = 0; k < sizeof(PonyMessage); k++)
{
ck += bp[k];
}
return (ck == 0);
}
void SocketServer::SocketEnd()
{
#ifdef WIN32
WSACleanup();
#endif
}
int SocketServer::SocketInit()
{
#ifdef WIN32
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 0);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
/* Tell the user that we couldn't find a useable */
/* winsock.dll. */
return 0;
/* Confirm that the Windows Sockets DLL supports 1.0.*/
/* Note that if the DLL supports versions greater */
/* than 1.0 in addition to 1.0, it will still return */
/* 1.0 in wVersion since that is the version we */
/* requested. */
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 0 )
{
// fprintf(stderr, "No usable WinSock DLL\n");
WSACleanup();
return 0;
}
/* The Windows Sockets DLL is acceptable. Proceed. */
#endif
return 1;
}
#ifdef WIN32
static struct {
int errval;
char *errstr;
} sockerrors[] = {
{WSANOTINITIALISED, "A successful WSAStartup must occur before using this function."},
{WSAENETDOWN,"The Windows Sockets implementation has detected that the network subsystem has failed."},
{WSAEFAULT,"The fromlen argument was invalid: the from buffer was too small to accommodate the peer address."},
{WSAEINTR," The (blocking) call was canceled using WSACancelBlockingCall."},
{WSAEAFNOSUPPORT,"The specified address family is not supported."},
{WSAEINPROGRESS,"A blocking Windows Sockets operation is in progress."},
{WSAEINVAL,"The socket has not been bound with bind"},
{WSAENOTCONN,"The socket is not connected (SOCK_STREAM only)"},
{WSAENOTSOCK,"The descriptor is not a socket"},
{WSAEOPNOTSUPP,"MSG_OOB was specified, but the socket is not of type SOCK_STREAM"},
{WSAESHUTDOWN,"The socket has been shut down; it is not possible to recvfrom on a socket after shutdown has been invoked with how set to 0 or 2."},
{WSAEWOULDBLOCK,"The socket is marked as nonblocking and the recvfrom operation would block"},
{WSAEMSGSIZE,"The datagram was too large to fit into the specified buffer and was truncated"},
{WSAECONNABORTED,"The virtual circuit was aborted due to timeout or other failure"},
{WSAECONNRESET,"The virtual circuit was reset by the remote side"},
{WSAEMFILE, "No more file descriptors are available."},
{WSAENOBUFS, "No buffer space is available. The socket cannot be created."},
{WSAEPROTONOSUPPORT, "The specified protocol is not supported."},
{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket."},
{WSAESOCKTNOSUPPORT, "The specified socket type is not supported in this address family."},
{0, NULL}
};
#endif
char *SocketServer::GetSockErrorString(int val)
{
#ifdef WIN32
int k;
for (k = 0; sockerrors[k].errstr != NULL; k++)
{
if (sockerrors[k].errval == val)
{
break;
}
}
return sockerrors[k].errstr;
#else
return 0;
#endif
}