1350 lines
35 KiB
C
1350 lines
35 KiB
C
|
/*
|
|||
|
* generic/dpTcp.c --
|
|||
|
*
|
|||
|
****************************************************************
|
|||
|
* This file implements the generic code for a tcp channel
|
|||
|
* driver in TCL 7.6 ONLY!
|
|||
|
*
|
|||
|
* The TCP code for Tcl 8.0 is in win/dpWinTCP.c and
|
|||
|
* unix/dpUnixTCP.c
|
|||
|
****************************************************************
|
|||
|
*
|
|||
|
* These are channels that are created by evaluating "dp_connect
|
|||
|
* tcp".
|
|||
|
*
|
|||
|
* This file was drerived from generic/dpUdp.c. See the
|
|||
|
* header of that file for more detailed information about
|
|||
|
* the way DP uses sockets.
|
|||
|
*
|
|||
|
* Copyright (c) 1995-1996 Cornell University.
|
|||
|
*
|
|||
|
* See the file "license.terms" for information on usage and redistribution
|
|||
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
#include "generic/dpInt.h"
|
|||
|
#include "generic/dpPort.h"
|
|||
|
|
|||
|
/*
|
|||
|
* The maximum length of the queue of pending connections to a
|
|||
|
* Tcp server socket.
|
|||
|
*/
|
|||
|
|
|||
|
#define DP_LISTEN_LIMIT 100
|
|||
|
|
|||
|
/*
|
|||
|
* The default send and receive buffer size.
|
|||
|
*/
|
|||
|
#define DP_TCP_SENDBUFSIZE 8192
|
|||
|
#define DP_TCP_RECVBUFSIZE 8192
|
|||
|
|
|||
|
typedef SocketState TcpState;
|
|||
|
|
|||
|
#define PEEK_MODE (1<<1) /* Read without consuming? */
|
|||
|
#define ASYNC_CONNECT (1<<2) /* Asynchronous connection? */
|
|||
|
#define IS_SERVER (1<<3) /* Is this a server Tcp socket? */
|
|||
|
|
|||
|
/*
|
|||
|
* Procedures that are used in this file only.
|
|||
|
*/
|
|||
|
|
|||
|
static int TcpBlockMode _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int mode));
|
|||
|
static int TcpInput _ANSI_ARGS_((ClientData instanceData,
|
|||
|
char *buf, int bufSize, int *errorCodePtr));
|
|||
|
static int TcpOutput _ANSI_ARGS_((ClientData instanceData,
|
|||
|
char *buf, int toWrite, int *errorCodePtr));
|
|||
|
static int TcpClose _ANSI_ARGS_((ClientData instanceData,
|
|||
|
Tcl_Interp *interp));
|
|||
|
static int TcpSetOption _ANSI_ARGS_((ClientData instanceData,
|
|||
|
Tcl_Interp *interp, char *optionName,
|
|||
|
char *optionValue));
|
|||
|
static int TcpGetOption _ANSI_ARGS_((ClientData instanceData,
|
|||
|
char *optionName, Tcl_DString *dsPtr));
|
|||
|
|
|||
|
static void TcpWatch _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int mask));
|
|||
|
static int TcpReady _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int mask));
|
|||
|
static Tcl_File TcpGetFile _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int direction));
|
|||
|
|
|||
|
static Tcl_Channel CreateClientChannel _ANSI_ARGS_((Tcl_Interp *interp,
|
|||
|
int destIpAddr, int destPort, int myIpAddr,
|
|||
|
int myPort, int async));
|
|||
|
static Tcl_Channel CreateServerChannel _ANSI_ARGS_((Tcl_Interp *interp,
|
|||
|
int myIpAddr, int myPort));
|
|||
|
static int SetDefaultOptions _ANSI_ARGS_((Tcl_Interp * interp,
|
|||
|
Tcl_Channel chan));
|
|||
|
static int DpTcpSetSocketOption _ANSI_ARGS_((TcpState *statePtr,
|
|||
|
int option, int value));
|
|||
|
static int DpTcpGetSocketOption _ANSI_ARGS_((TcpState *statePtr,
|
|||
|
int option, int *valuePtr));
|
|||
|
static int DpSetAddress _ANSI_ARGS_((TcpState *statePtr));
|
|||
|
|
|||
|
|
|||
|
static Tcl_ChannelType tcpChannelType = {
|
|||
|
"tcp", /* Name of channel */
|
|||
|
TcpBlockMode, /* Proc to set blocking mode on socket */
|
|||
|
TcpClose, /* Proc to close a socket */
|
|||
|
TcpInput, /* Proc to get input from a socket */
|
|||
|
TcpOutput, /* Proc to send output to a socket */
|
|||
|
NULL, /* Can't seek on a socket! */
|
|||
|
TcpSetOption, /* Proc to set a socket option */
|
|||
|
TcpGetOption, /* Proc to set a socket option */
|
|||
|
TcpWatch, /* Proc called to set event loop wait params */
|
|||
|
TcpReady, /* Proc called to check if socket has input */
|
|||
|
TcpGetFile /* Proc to return a handle assoc with socket */
|
|||
|
};
|
|||
|
|
|||
|
static int tcpCount = 0; /* Number of tcp files opened -- used to
|
|||
|
* generate unique ids for channels */
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* DpOpenTcpChannel --
|
|||
|
*
|
|||
|
* Opens a new channel that uses the TCP protocol.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns a pointer to the newly created Tcl_Channel. This
|
|||
|
* is the structure with all the function pointers Tcl needs
|
|||
|
* to communicate with (read, write, close, etc) the channel.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* A socket is created with the specified port. No other
|
|||
|
* socket can use that port until this channel is closed.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
Tcl_Channel
|
|||
|
DpOpenTcpChannel(interp, argc, argv)
|
|||
|
Tcl_Interp *interp; /* For error reporting; can be NULL. */
|
|||
|
int argc; /* Number of arguments. */
|
|||
|
char **argv; /* Argument strings. */
|
|||
|
{
|
|||
|
Tcl_Channel chan;
|
|||
|
TcpState *statePtr = NULL;
|
|||
|
char channelName[20];
|
|||
|
int i;
|
|||
|
|
|||
|
/*
|
|||
|
* The default values for the value-option pairs
|
|||
|
*/
|
|||
|
int async = 0;
|
|||
|
int destIpAddr = 0;
|
|||
|
int destPort = 0;
|
|||
|
int myIpAddr = DP_INADDR_ANY;
|
|||
|
int myPort = 0;
|
|||
|
int isServer = 0;
|
|||
|
|
|||
|
/*
|
|||
|
* Flags to indicate that a certain option has been set by the
|
|||
|
* command line
|
|||
|
*/
|
|||
|
int setAsync = 0;
|
|||
|
int setHost = 0;
|
|||
|
int setMyPort = 0;
|
|||
|
int setPort = 0;
|
|||
|
|
|||
|
|
|||
|
for (i=0; i<argc; i+=2) {
|
|||
|
int v = i+1;
|
|||
|
size_t len = strlen(argv[i]);
|
|||
|
|
|||
|
if (strncmp(argv[i], "-async", len)==0) {
|
|||
|
if (v==argc) {goto arg_missing;}
|
|||
|
|
|||
|
if (Tcl_GetBoolean(interp, argv[v], &async) != TCL_OK) {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
setAsync = 1;
|
|||
|
}
|
|||
|
else if (strncmp(argv[i], "-host", len)==0) {
|
|||
|
if (v==argc) {goto arg_missing;}
|
|||
|
|
|||
|
if (!DpHostToIpAddr (argv[v], &destIpAddr)) {
|
|||
|
Tcl_AppendResult (interp, "Illegal value for -host \"",
|
|||
|
argv[v], "\"", NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
setHost = 1;
|
|||
|
}
|
|||
|
else if (strncmp(argv[i], "-myaddr", len)==0) {
|
|||
|
if (v==argc) {goto arg_missing;}
|
|||
|
|
|||
|
if (strcmp (argv[v], "any") == 0) {
|
|||
|
myIpAddr = DP_INADDR_ANY;
|
|||
|
} else if (!DpHostToIpAddr (argv[v], &myIpAddr)) {
|
|||
|
Tcl_AppendResult (interp, "Illegal value for -myaddr \"",
|
|||
|
argv[v], "\"", NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (strncmp(argv[i], "-myport", len)==0) {
|
|||
|
if (v==argc) {goto arg_missing;}
|
|||
|
|
|||
|
if (Tcl_GetInt(interp, argv[v], &myPort) != TCL_OK) {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
setMyPort = 1;
|
|||
|
}
|
|||
|
else if (strncmp(argv[i], "-port", len)==0) {
|
|||
|
if (v==argc) {goto arg_missing;}
|
|||
|
|
|||
|
if (Tcl_GetInt(interp, argv[v], &destPort) != TCL_OK) {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
setPort = 1;
|
|||
|
}
|
|||
|
else if (strncmp(argv[i], "-server", len)==0) {
|
|||
|
if (v==argc) {goto arg_missing;}
|
|||
|
|
|||
|
if (Tcl_GetBoolean(interp, argv[v], &isServer) != TCL_OK) {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
Tcl_AppendResult(interp, "unknown option \"",
|
|||
|
argv[i], "\", must be -async, -host, -myaddr, -myport ",
|
|||
|
"-port or -server", NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Check the options that must or must not be specified, depending on
|
|||
|
* the -server option.
|
|||
|
*/
|
|||
|
|
|||
|
if (isServer) {
|
|||
|
if (!setMyPort) {
|
|||
|
Tcl_AppendResult(interp, "option -myport must be specified ",
|
|||
|
"for servers",
|
|||
|
NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
if (setAsync) {
|
|||
|
Tcl_AppendResult(interp, "option -async is not valid for servers",
|
|||
|
NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
if (setHost) {
|
|||
|
Tcl_AppendResult(interp, "option -host is not valid for servers",
|
|||
|
NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
if (setPort) {
|
|||
|
Tcl_AppendResult(interp, "option -port is not valid for servers",
|
|||
|
NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (!setPort) {
|
|||
|
Tcl_AppendResult(interp, "option -port must be specified ",
|
|||
|
"for clients",
|
|||
|
NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
if (!setHost) {
|
|||
|
Tcl_AppendResult(interp, "option -host must be specified",
|
|||
|
NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Create a new socket and wrap it in a channel.
|
|||
|
*/
|
|||
|
|
|||
|
if (isServer) {
|
|||
|
chan = CreateServerChannel(interp, myIpAddr, myPort);
|
|||
|
} else {
|
|||
|
chan = CreateClientChannel(interp, destIpAddr, destPort, myIpAddr,
|
|||
|
myPort, async);
|
|||
|
}
|
|||
|
if (chan == NULL) {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
Tcl_RegisterChannel(interp, chan);
|
|||
|
|
|||
|
if (SetDefaultOptions(interp, chan) != TCL_OK) {
|
|||
|
DpClose(interp, chan);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
return chan;
|
|||
|
|
|||
|
arg_missing:
|
|||
|
Tcl_AppendResult(interp, "value for \"", argv[argc-1], "\" missing", NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* Dp_TcpAccept --
|
|||
|
*
|
|||
|
* This procedure blocks until a client is connected to a given
|
|||
|
* Tcp server channel.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* On success, returns the Tcl_Channel of the connection. On failure,
|
|||
|
* returns NULL.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* The first client on the "queue of connected clients" (maintained
|
|||
|
* by the OS) is removed from the queue.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
Tcl_Channel
|
|||
|
Dp_TcpAccept(interp, channelId)
|
|||
|
Tcl_Interp *interp; /* For error reporting. */
|
|||
|
char *channelId; /* Name of a server Tcp channel. */
|
|||
|
{
|
|||
|
struct sockaddr_in destSockAddr; /* Address of client host. */
|
|||
|
TcpState *svrStatePtr = NULL; /* State of the server socket. */
|
|||
|
TcpState *statePtr = NULL; /* State of the new socket. */
|
|||
|
int sock;
|
|||
|
Tcl_Channel chan;
|
|||
|
int len, mode;
|
|||
|
char channelName[20];
|
|||
|
int argc, ip;
|
|||
|
char *argv[2], *argRet;
|
|||
|
char ipStr[16];
|
|||
|
|
|||
|
chan = Tcl_GetChannel(interp, channelId, &mode);
|
|||
|
if (chan == NULL) {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (Tcl_GetChannelType(chan) != &tcpChannelType) {
|
|||
|
Tcl_AppendResult(interp, "\"", channelId,
|
|||
|
"\" must be a TCP server channel", NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
svrStatePtr = (TcpState *)Tcl_GetChannelInstanceData(chan);
|
|||
|
|
|||
|
if (!(svrStatePtr->flags & IS_SERVER)) {
|
|||
|
Tcl_AppendResult(interp, "\"", channelId,
|
|||
|
"\" is a TCP client channel, not a server", NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
len = sizeof(destSockAddr);
|
|||
|
sock = accept(svrStatePtr->sock, (struct sockaddr *)&destSockAddr, &len);
|
|||
|
if (sock < 0) {
|
|||
|
Tcl_AppendResult(interp, "couldn't accept from socket: ",
|
|||
|
Tcl_PosixError(interp), (char *) NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
statePtr = (TcpState *) ckalloc(sizeof(TcpState));
|
|||
|
statePtr->flags = 0;
|
|||
|
statePtr->sock = sock;
|
|||
|
statePtr->sockFile = Tcl_GetFile((ClientData)sock, DP_SOCKET);
|
|||
|
statePtr->interp = interp;
|
|||
|
statePtr->myIpAddr = 0;
|
|||
|
statePtr->myPort = 0;
|
|||
|
statePtr->destIpAddr = 0;
|
|||
|
statePtr->destPort = 0;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
sprintf(channelName, "tcp%d", tcpCount++);
|
|||
|
chan = Tcl_CreateChannel(&tcpChannelType, channelName,
|
|||
|
(ClientData)statePtr, TCL_READABLE | TCL_WRITABLE);
|
|||
|
|
|||
|
Tcl_RegisterChannel(interp, chan);
|
|||
|
|
|||
|
if (SetDefaultOptions(interp, chan) != TCL_OK) {
|
|||
|
DpClose(interp, chan);
|
|||
|
ckfree((char *)statePtr);
|
|||
|
Tcl_AppendResult(interp, "unable to set default options on accepted socket: ",
|
|||
|
Tcl_PosixError(interp), (char *) NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Construct return value list { <chanID> <ipAdr> }
|
|||
|
*/
|
|||
|
|
|||
|
DpTcpGetSocketOption(statePtr, DP_REMOTEIPADDR, &ip);
|
|||
|
sprintf(ipStr, "%d.%d.%d.%d", (ip>>24)&0xFF, (ip>>16)&0xFF,
|
|||
|
(ip>>8)&0xFF, ip&0xFF);
|
|||
|
argc = 2;
|
|||
|
argv[0] = channelName;
|
|||
|
argv[1] = ipStr;
|
|||
|
argRet = Tcl_Merge(argc, argv);
|
|||
|
Tcl_AppendResult(interp, argRet, (char *) NULL);
|
|||
|
ckfree((char *)argRet);
|
|||
|
|
|||
|
return chan;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* TcpBlockMode --
|
|||
|
*
|
|||
|
* Sets the tcp socket to blocking or non-blocking. Just
|
|||
|
* a wrapper around the platform specific function.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Zero if the operation was successful, or a nonzero POSIX
|
|||
|
* error code if the operation failed.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
TcpBlockMode (instanceData, mode)
|
|||
|
ClientData instanceData; /* Pointer to tcpState struct */
|
|||
|
int mode; /* TCL_MODE_BLOCKING or TCL_MODE_NONBLOCKING */
|
|||
|
{
|
|||
|
TcpState *statePtr = (TcpState *)instanceData;
|
|||
|
|
|||
|
if (mode == TCL_MODE_BLOCKING) {
|
|||
|
return DpTcpSetSocketOption(statePtr, DP_BLOCK, 1);
|
|||
|
} else {
|
|||
|
return DpTcpSetSocketOption(statePtr, DP_BLOCK, 0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* TcpClose --
|
|||
|
*
|
|||
|
* This function is called by the Tcl channel driver when the
|
|||
|
* caller want to close the socket. It releases the instanceData
|
|||
|
* and closes the scoket. All queued output will have been flushed
|
|||
|
* to the device before this function is called.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Zero for success, otherwise a nonzero POSIX error code and,
|
|||
|
* if interp is not NULL, an error message in interp->result
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
TcpClose (instanceData, interp)
|
|||
|
ClientData instanceData; /* (in) Pointer to tcpState struct */
|
|||
|
Tcl_Interp *interp; /* (in) For error reporting */
|
|||
|
{
|
|||
|
TcpState *statePtr = (TcpState *)instanceData;
|
|||
|
int result;
|
|||
|
|
|||
|
result = DppCloseSocket(statePtr->sock);
|
|||
|
if ((result != 0) && (interp != NULL)) {
|
|||
|
DppGetErrno();
|
|||
|
Tcl_SetResult(interp, Tcl_PosixError(interp), TCL_STATIC);
|
|||
|
}
|
|||
|
|
|||
|
ckfree((char *)statePtr);
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* TcpInput --
|
|||
|
*
|
|||
|
* This function is called by the Tcl channel driver whenever the
|
|||
|
* user wants to get input from the TCP socket. If the socket
|
|||
|
* has some data available but less than requested by the bufSize
|
|||
|
* argument, we only read as much data as is available and return
|
|||
|
* without blocking. If the socket has no data available
|
|||
|
* whatsoever and is blocking, we block until at least one byte
|
|||
|
* of data can be read from the socket.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* A nonnegative integer indicating how many bytes were read, or
|
|||
|
* DP_SOCKET_ERROR in case of error (with errorCodePtr set to the
|
|||
|
* POSIX error code).
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
TcpInput (instanceData, buf, bufSize, errorCodePtr)
|
|||
|
ClientData instanceData; /* (in) Pointer to tcpState struct */
|
|||
|
char *buf; /* (in/out) Buffer to fill */
|
|||
|
int bufSize; /* (in) Size of buffer */
|
|||
|
int *errorCodePtr; /* (out) POSIX error code (if any) */
|
|||
|
{
|
|||
|
TcpState *statePtr = (TcpState *)instanceData;
|
|||
|
int result;
|
|||
|
|
|||
|
result = recv(statePtr->sock, buf, bufSize, 0);
|
|||
|
|
|||
|
if (result < 0) {
|
|||
|
if (DppGetErrno() == ECONNRESET) {
|
|||
|
/*
|
|||
|
* Turn ECONNRESET into a soft EOF condition.
|
|||
|
*/
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
*errorCodePtr = Tcl_GetErrno();
|
|||
|
return -1;
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* TcpOutput --
|
|||
|
*
|
|||
|
* This function is called by the Tcl channel driver whenever the
|
|||
|
* user wants to send output to the TCP socket. The function
|
|||
|
* writes toWrite bytes from buf to the socket.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* On success, returns a nonnegative integer indicating how many
|
|||
|
* bytes were written to the socket. The return value is normally
|
|||
|
* the same as toWrite, but may be less in some cases such as if
|
|||
|
* the output operation is interrupted by a signal.
|
|||
|
*
|
|||
|
* On failure, returns DP_SOCKET_ERROR;
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
TcpOutput (instanceData, buf, toWrite, errorCodePtr)
|
|||
|
ClientData instanceData; /* (in) Pointer to tcpState struct */
|
|||
|
char *buf; /* (in) Buffer to write */
|
|||
|
int toWrite; /* (in) Number of bytes to write */
|
|||
|
int *errorCodePtr; /* (out) POSIX error code (if any) */
|
|||
|
{
|
|||
|
TcpState *statePtr = (TcpState *)instanceData;
|
|||
|
int result;
|
|||
|
|
|||
|
result = send(statePtr->sock, buf, toWrite, 0);
|
|||
|
|
|||
|
if (result < 0) {
|
|||
|
result = DP_SOCKET_ERROR;
|
|||
|
*errorCodePtr = DppGetErrno();
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* TcpSetOption --
|
|||
|
*
|
|||
|
* This function is called by the Tcl channel driver
|
|||
|
* whenever Tcl evaluates and fconfigure call to set
|
|||
|
* some property of the tcp socket (e.g., the buffer
|
|||
|
* size). The valid options are "sendBuffer" and
|
|||
|
* "recvBuffer"
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Standard Tcl return value.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Depends on the option. Generally changes the maximum
|
|||
|
* message size that can be sent/received.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
TcpSetOption (instanceData, interp, optionName, optionValue)
|
|||
|
ClientData instanceData;
|
|||
|
Tcl_Interp *interp;
|
|||
|
char *optionName;
|
|||
|
char *optionValue;
|
|||
|
{
|
|||
|
int option;
|
|||
|
int value;
|
|||
|
TcpState *statePtr = (TcpState *)instanceData;
|
|||
|
|
|||
|
/*
|
|||
|
* Set the option specified by optionName
|
|||
|
*/
|
|||
|
if (optionName[0] == '-') {
|
|||
|
option = DpTranslateOption(optionName+1);
|
|||
|
} else {
|
|||
|
option = -1;
|
|||
|
}
|
|||
|
switch (option) {
|
|||
|
case DP_KEEP_ALIVE:
|
|||
|
case DP_REUSEADDR:
|
|||
|
if (Tcl_GetBoolean(interp, optionValue, &value) != TCL_OK) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
return DpTcpSetSocketOption(statePtr, option, value);
|
|||
|
|
|||
|
case DP_LINGER:
|
|||
|
if (Tcl_GetInt(interp, optionValue, &value) != TCL_OK) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (value <=0) {
|
|||
|
Tcl_AppendResult(interp, "linger value must be > 0", NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
return DpTcpSetSocketOption(statePtr, option, value);
|
|||
|
|
|||
|
case DP_RECV_BUFFER_SIZE:
|
|||
|
case DP_SEND_BUFFER_SIZE:
|
|||
|
if (Tcl_GetInt(interp, optionValue, &value) != TCL_OK) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (value <=0) {
|
|||
|
Tcl_AppendResult(interp, "Buffer size must be > 0", NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
return DpTcpSetSocketOption(statePtr, option, value);
|
|||
|
|
|||
|
default:
|
|||
|
Tcl_AppendResult (interp, "bad option \"", optionName,
|
|||
|
"\": must be -keepalive, -linger, -recvbuffer, -reuseaddr, ",
|
|||
|
"-sendBuffer or a standard fconfigure option", NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* TcpGetOption --
|
|||
|
*
|
|||
|
* This function is called by the Tcl channel code to
|
|||
|
* retrieve a parameter of the socket (e.g., a buffer size).
|
|||
|
* The valid options are "sendBuffer" and "recvBuffer"
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* A standard Tcl result
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
TcpGetOption (instanceData,
|
|||
|
optionName, dsPtr)
|
|||
|
ClientData instanceData;
|
|||
|
char *optionName;
|
|||
|
Tcl_DString *dsPtr;
|
|||
|
{
|
|||
|
int option;
|
|||
|
int value, rc;
|
|||
|
char str[256];
|
|||
|
TcpState *statePtr = (TcpState *)instanceData;
|
|||
|
|
|||
|
/*
|
|||
|
* If optionName is NULL, then return all options in option-value
|
|||
|
* pairs.
|
|||
|
*/
|
|||
|
|
|||
|
if (optionName == NULL) {
|
|||
|
Tcl_DStringAppend (dsPtr, " -keepAlive ", -1);
|
|||
|
TcpGetOption(instanceData, "-keepAlive", dsPtr);
|
|||
|
Tcl_DStringAppend (dsPtr, " -linger ", -1);
|
|||
|
TcpGetOption(instanceData, "-linger", dsPtr);
|
|||
|
Tcl_DStringAppend (dsPtr, " -recvBuffer ", -1);
|
|||
|
TcpGetOption(instanceData, "-recvBuffer", dsPtr);
|
|||
|
Tcl_DStringAppend (dsPtr, " -reuseAddr ", -1);
|
|||
|
TcpGetOption(instanceData, "-reuseAddr", dsPtr);
|
|||
|
Tcl_DStringAppend (dsPtr, " -sendBuffer ", -1);
|
|||
|
TcpGetOption(instanceData, "-sendBuffer", dsPtr);
|
|||
|
Tcl_DStringAppend (dsPtr, " -myIpAddr ", -1);
|
|||
|
TcpGetOption(instanceData, "-myIpAddr", dsPtr);
|
|||
|
Tcl_DStringAppend (dsPtr, " -myport ", -1);
|
|||
|
TcpGetOption(instanceData, "-myport", dsPtr);
|
|||
|
Tcl_DStringAppend (dsPtr, " -destIpAddr ", -1);
|
|||
|
TcpGetOption(instanceData, "-destIpAddr", dsPtr);
|
|||
|
Tcl_DStringAppend (dsPtr, " -destport ", -1);
|
|||
|
TcpGetOption(instanceData, "-destport", dsPtr);
|
|||
|
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Retrive the value of the option specified by optionName
|
|||
|
*/
|
|||
|
|
|||
|
if (optionName[0] == '-') {
|
|||
|
option = DpTranslateOption(optionName+1);
|
|||
|
} else {
|
|||
|
option = -1;
|
|||
|
}
|
|||
|
|
|||
|
switch (option) {
|
|||
|
case DP_LINGER:
|
|||
|
case DP_RECV_BUFFER_SIZE:
|
|||
|
case DP_SEND_BUFFER_SIZE:
|
|||
|
case DP_MYPORT:
|
|||
|
case DP_REMOTEPORT:
|
|||
|
if (DpTcpGetSocketOption(statePtr, option, &value) != 0) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
sprintf (str, "%d", value);
|
|||
|
Tcl_DStringAppend(dsPtr, str, -1);
|
|||
|
break;
|
|||
|
|
|||
|
case DP_KEEP_ALIVE:
|
|||
|
case DP_REUSEADDR:
|
|||
|
if (DpTcpGetSocketOption(statePtr, option, &value) != 0) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (value) {
|
|||
|
/*
|
|||
|
* Some systems returns a non-zero value
|
|||
|
* (not necessarily 1) to indicate "true".
|
|||
|
*/
|
|||
|
value = 1;
|
|||
|
}
|
|||
|
sprintf (str, "%d", value);
|
|||
|
Tcl_DStringAppend(dsPtr, str, -1);
|
|||
|
break;
|
|||
|
|
|||
|
case DP_MYIPADDR:
|
|||
|
case DP_REMOTEIPADDR:
|
|||
|
if (DpTcpGetSocketOption(statePtr, option, &value) != 0) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
sprintf(str, "%d.%d.%d.%d", (value>>24)&0xFF, (value>>16)&0xFF,
|
|||
|
(value>>8)&0xFF, value&0xFF);
|
|||
|
Tcl_DStringAppend(dsPtr, str, -1);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
{
|
|||
|
char errStr[128];
|
|||
|
sprintf(errStr, "bad option \"%s\": must be -blocking,"
|
|||
|
" -buffering, -buffersize, -eofchar, -translation,"
|
|||
|
" or a channel type specific option", optionName);
|
|||
|
Tcl_DStringAppend(dsPtr, errStr, -1);
|
|||
|
}
|
|||
|
Tcl_SetErrno (EINVAL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* TcpWatch --
|
|||
|
*
|
|||
|
* Gives a short overview (a few sentences), what other
|
|||
|
* functions are related to this one.
|
|||
|
*
|
|||
|
* All changes to the module made that decrease resource usage,
|
|||
|
* but make the function harder to understand, modify, and debug.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Global variables touched.
|
|||
|
* I/O operations performed.
|
|||
|
* Delayed effects.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
TcpWatch (instanceData, mask)
|
|||
|
ClientData instanceData;
|
|||
|
int mask;
|
|||
|
{
|
|||
|
TcpState *infoPtr = (TcpState *) instanceData;
|
|||
|
|
|||
|
Tcl_WatchFile(infoPtr->sockFile, mask);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* TcpReady --
|
|||
|
*
|
|||
|
* Called by the notifier to check whether events of interest are
|
|||
|
* present on the channel.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns OR-ed combination of TCL_READABLE, TCL_WRITABLE and
|
|||
|
* TCL_EXCEPTION to indicate which events of interest are present.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
TcpReady (instanceData, mask)
|
|||
|
ClientData instanceData;
|
|||
|
int mask;
|
|||
|
{
|
|||
|
TcpState *statePtr = (TcpState *) instanceData;
|
|||
|
|
|||
|
return Tcl_FileReady(statePtr->sockFile, mask);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* TcpGetFile --
|
|||
|
*
|
|||
|
* Called from Tcl_GetChannelFile to retrieve the handle
|
|||
|
* from inside a TCP socket based channel.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* TCL_OK
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
static Tcl_File
|
|||
|
TcpGetFile(instanceData, direction)
|
|||
|
ClientData instanceData;
|
|||
|
int direction;
|
|||
|
{
|
|||
|
TcpState *statePtr = (TcpState *)instanceData;
|
|||
|
|
|||
|
return statePtr->sockFile;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* CreateClientChannel --
|
|||
|
*
|
|||
|
* This function opens a new socket in client mode
|
|||
|
* and initializes the TcpState structure.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns a new TcpState, or NULL with an error in interp->result,
|
|||
|
* if interp is not NULL.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Opens a socket.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static Tcl_Channel
|
|||
|
CreateClientChannel(interp, destIpAddr, destPort, myIpAddr, myPort, async)
|
|||
|
Tcl_Interp *interp; /* For error reporting; can be NULL. */
|
|||
|
int destIpAddr; /* IP address of the host to connect to. */
|
|||
|
int destPort; /* Port number to open. */
|
|||
|
int myIpAddr; /* Client-side address */
|
|||
|
int myPort; /* Client-side port */
|
|||
|
int async; /* If nonzero and creating a client socket,
|
|||
|
* attempt to do an async connect. Otherwise
|
|||
|
* do a synchronous connect or bind. */
|
|||
|
{
|
|||
|
int status, sock, asyncConnect, origState, len;
|
|||
|
struct sockaddr_in destSockAddr; /* socket address for host */
|
|||
|
struct sockaddr_in mySockAddr; /* Socket address for client */
|
|||
|
TcpState *statePtr = NULL;
|
|||
|
char channelName[20];
|
|||
|
|
|||
|
sock = -1;
|
|||
|
origState = 0;
|
|||
|
|
|||
|
memset((char *)&destSockAddr, 0, sizeof(destSockAddr));
|
|||
|
destSockAddr.sin_family = AF_INET;
|
|||
|
destSockAddr.sin_port = htons((short)destPort);
|
|||
|
destSockAddr.sin_addr.s_addr = htonl(destIpAddr);
|
|||
|
|
|||
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|||
|
if (sock < 0) {
|
|||
|
goto error;
|
|||
|
}
|
|||
|
|
|||
|
asyncConnect = 0;
|
|||
|
status = 0;
|
|||
|
|
|||
|
memset((char *)&mySockAddr, 0, sizeof(mySockAddr));
|
|||
|
mySockAddr.sin_family = AF_INET;
|
|||
|
mySockAddr.sin_port = htons((unsigned short)myPort);
|
|||
|
if (myIpAddr == DP_INADDR_ANY) {
|
|||
|
mySockAddr.sin_addr.s_addr = INADDR_ANY;
|
|||
|
} else {
|
|||
|
mySockAddr.sin_addr.s_addr = htonl(myIpAddr);
|
|||
|
}
|
|||
|
|
|||
|
status = bind(sock, (struct sockaddr *) &mySockAddr,
|
|||
|
sizeof(mySockAddr));
|
|||
|
if (status < 0) {
|
|||
|
goto error;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Attempt to connect. The connect may fail at present with an
|
|||
|
* EINPROGRESS but at a later time it will complete. The caller
|
|||
|
* will set up a file handler on the socket if she is interested in
|
|||
|
* being informed when the connect completes.
|
|||
|
*/
|
|||
|
|
|||
|
if (async) {
|
|||
|
status = DppSetBlock(sock, 0);
|
|||
|
if (status < 0) {
|
|||
|
goto error;
|
|||
|
}
|
|||
|
} else {
|
|||
|
status = 0;
|
|||
|
}
|
|||
|
|
|||
|
if (status > -1) {
|
|||
|
status = connect(sock, (struct sockaddr *) &destSockAddr,
|
|||
|
sizeof(destSockAddr));
|
|||
|
if (status < 0) {
|
|||
|
if (DppGetErrno() == ASYNC_CONNECT_ERROR) {
|
|||
|
asyncConnect = 1;
|
|||
|
status = 0;
|
|||
|
} else {
|
|||
|
goto error;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Allocate a new TcpState for this socket.
|
|||
|
*/
|
|||
|
|
|||
|
statePtr = (TcpState *)ckalloc(sizeof(TcpState));
|
|||
|
statePtr->flags = 0;
|
|||
|
if (asyncConnect) {
|
|||
|
statePtr->flags = ASYNC_CONNECT;
|
|||
|
}
|
|||
|
statePtr->sock = sock;
|
|||
|
statePtr->sockFile = Tcl_GetFile((ClientData)sock, DP_SOCKET);
|
|||
|
statePtr->interp = interp;
|
|||
|
/*
|
|||
|
* These variables are set whem they are needed.
|
|||
|
* A call to fconfigure will prompt the call to
|
|||
|
* SetAddress().
|
|||
|
*/
|
|||
|
statePtr->myIpAddr = 0;
|
|||
|
statePtr->myPort = 0;
|
|||
|
statePtr->destIpAddr = 0;
|
|||
|
statePtr->destPort = 0;
|
|||
|
|
|||
|
sprintf(channelName, "tcp%d", tcpCount++);
|
|||
|
statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
|
|||
|
(ClientData)statePtr, TCL_READABLE | TCL_WRITABLE);
|
|||
|
|
|||
|
return statePtr->channel;
|
|||
|
|
|||
|
error:
|
|||
|
/*
|
|||
|
* Call DppGetErrno() to translate the error into a POSIX code.
|
|||
|
*/
|
|||
|
DppGetErrno();
|
|||
|
if (interp != NULL) {
|
|||
|
Tcl_AppendResult(interp, "couldn't open socket: ",
|
|||
|
Tcl_PosixError(interp), (char *) NULL);
|
|||
|
}
|
|||
|
if (statePtr != NULL) {
|
|||
|
ckfree((char*)statePtr);
|
|||
|
}
|
|||
|
if (sock != -1) {
|
|||
|
close(sock);
|
|||
|
}
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* CreateServerChannel --
|
|||
|
*
|
|||
|
* This function opens a new socket in server mode and
|
|||
|
* initializes the TcpState structure.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns a new TcpState, or NULL with an error in interp->result,
|
|||
|
* if interp is not NULL.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Opens a socket.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static Tcl_Channel
|
|||
|
CreateServerChannel(interp, myIpAddr, myPort)
|
|||
|
Tcl_Interp *interp; /* For error reporting; can be NULL. */
|
|||
|
int myIpAddr; /* Address of the server. */
|
|||
|
int myPort; /* Port number to listen to */
|
|||
|
{
|
|||
|
int status, sock, len;
|
|||
|
struct sockaddr_in mySockAddr; /* socket address to use. */
|
|||
|
TcpState *statePtr = NULL;
|
|||
|
char channelName[20];
|
|||
|
|
|||
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|||
|
if (sock < 0) {
|
|||
|
goto error;
|
|||
|
}
|
|||
|
|
|||
|
memset((char *)&mySockAddr, 0, sizeof(mySockAddr));
|
|||
|
mySockAddr.sin_family = AF_INET;
|
|||
|
if (myIpAddr == DP_INADDR_ANY) {
|
|||
|
mySockAddr.sin_addr.s_addr = INADDR_ANY;
|
|||
|
} else {
|
|||
|
mySockAddr.sin_addr.s_addr = htonl(myIpAddr);
|
|||
|
}
|
|||
|
mySockAddr.sin_port = htons((unsigned short)myPort);
|
|||
|
|
|||
|
status = bind(sock, (struct sockaddr *) &mySockAddr,
|
|||
|
sizeof(mySockAddr));
|
|||
|
if (status >= 0) {
|
|||
|
status = listen(sock, DP_LISTEN_LIMIT);
|
|||
|
}
|
|||
|
if (status < 0) {
|
|||
|
goto error;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Allocate a new TcpState for this server socket.
|
|||
|
*/
|
|||
|
|
|||
|
statePtr = (TcpState *)ckalloc(sizeof(TcpState));
|
|||
|
statePtr->flags = IS_SERVER;
|
|||
|
statePtr->sock = sock;
|
|||
|
statePtr->sockFile = Tcl_GetFile((ClientData)sock, DP_SOCKET);
|
|||
|
statePtr->interp = interp;
|
|||
|
|
|||
|
/*
|
|||
|
* These are set in DpSetAddress.
|
|||
|
*/
|
|||
|
statePtr->destIpAddr = 0;
|
|||
|
statePtr->destPort = 0;
|
|||
|
statePtr->myIpAddr = 0;
|
|||
|
statePtr->myPort = 0;
|
|||
|
|
|||
|
sprintf(channelName, "tcp%d", tcpCount++);
|
|||
|
statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
|
|||
|
(ClientData)statePtr, TCL_READABLE | TCL_WRITABLE);
|
|||
|
|
|||
|
return statePtr->channel;
|
|||
|
|
|||
|
error:
|
|||
|
/*
|
|||
|
* Call DppGetErrno() to translate the error into a POSIX code.
|
|||
|
*/
|
|||
|
DppGetErrno();
|
|||
|
if (interp != NULL) {
|
|||
|
Tcl_AppendResult(interp, "error opening socket: ",
|
|||
|
Tcl_PosixError(interp), (char *) NULL);
|
|||
|
}
|
|||
|
if (statePtr != NULL) {
|
|||
|
ckfree((char*)statePtr);
|
|||
|
}
|
|||
|
if (sock != -1) {
|
|||
|
close(sock);
|
|||
|
}
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* SetDefaultOptions --
|
|||
|
*
|
|||
|
* Sets the default options for a Tcp socket.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* A standard Tcl result.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Some options of this socket are changed.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
SetDefaultOptions(interp, chan)
|
|||
|
Tcl_Interp *interp;
|
|||
|
Tcl_Channel chan;
|
|||
|
{
|
|||
|
TcpState *statePtr = (TcpState *) Tcl_GetChannelInstanceData(chan);
|
|||
|
|
|||
|
if (DpTcpSetSocketOption(statePtr, DP_RECV_BUFFER_SIZE,
|
|||
|
DP_TCP_RECVBUFSIZE) != TCL_OK) {
|
|||
|
goto error;
|
|||
|
}
|
|||
|
if (DpTcpSetSocketOption(statePtr, DP_REUSEADDR, 1) != TCL_OK) {
|
|||
|
goto error;
|
|||
|
}
|
|||
|
if (DpTcpSetSocketOption(statePtr, DP_KEEP_ALIVE, 0) != TCL_OK) {
|
|||
|
goto error;
|
|||
|
}
|
|||
|
if (DpTcpSetSocketOption(statePtr, DP_SEND_BUFFER_SIZE,
|
|||
|
DP_TCP_SENDBUFSIZE) != TCL_OK) {
|
|||
|
goto error;
|
|||
|
}
|
|||
|
|
|||
|
if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") !=
|
|||
|
TCL_OK) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (Tcl_SetChannelOption(interp, chan, "-buffering", "none") !=
|
|||
|
TCL_OK) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (Tcl_SetChannelOption(interp, chan, "-eofchar", "") != TCL_OK) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
return TCL_OK;
|
|||
|
|
|||
|
error:
|
|||
|
Tcl_AppendResult(interp, "couldn't set default options for socket: ",
|
|||
|
Tcl_PosixError(interp), (char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* DpTcpSetSocketOption --
|
|||
|
*
|
|||
|
* Sets a socket option. Note that we hardcode a linger
|
|||
|
* time of 30 seconds.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Zero if the operation was successful, or a nonzero POSIX
|
|||
|
* error code if the operation failed.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
DpTcpSetSocketOption(statePtr, option, value)
|
|||
|
TcpState *statePtr; /* (in) TcpState structure */
|
|||
|
int option; /* (in) Option to set */
|
|||
|
int value; /* (in) new value for option */
|
|||
|
{
|
|||
|
int sock, result;
|
|||
|
struct linger l;
|
|||
|
|
|||
|
sock = statePtr->sock;
|
|||
|
switch (option) {
|
|||
|
case DP_BLOCK:
|
|||
|
result = DppSetBlock(sock, value);
|
|||
|
break;
|
|||
|
case DP_KEEP_ALIVE:
|
|||
|
result = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&value,
|
|||
|
sizeof(value));
|
|||
|
break;
|
|||
|
case DP_LINGER:
|
|||
|
l.l_onoff = value;
|
|||
|
l.l_linger = 30;
|
|||
|
result = setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&l,
|
|||
|
sizeof(struct linger));
|
|||
|
break;
|
|||
|
case DP_RECV_BUFFER_SIZE:
|
|||
|
result = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&value,
|
|||
|
sizeof(value));
|
|||
|
break;
|
|||
|
case DP_REUSEADDR:
|
|||
|
result = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&value,
|
|||
|
sizeof(value));
|
|||
|
break;
|
|||
|
case DP_SEND_BUFFER_SIZE:
|
|||
|
result = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&value,
|
|||
|
sizeof(value));
|
|||
|
break;
|
|||
|
default:
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
if (result != 0) {
|
|||
|
return DppGetErrno();
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* DpTcpGetSocketOption --
|
|||
|
*
|
|||
|
* Sets a socket option. The allowable options for Tcp
|
|||
|
* sockets are
|
|||
|
* DP_SEND_BUFFER_SIZE (int)
|
|||
|
* DP_RECV_BUFFER_SIZE (int)
|
|||
|
* Note that we can't determine whether a socket is blocking,
|
|||
|
* so DP_BLOCK is not allowed.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Zero if the operation was successful, or a nonzero POSIX
|
|||
|
* error code if the operation failed.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
DpTcpGetSocketOption (statePtr, option, valuePtr)
|
|||
|
TcpState *statePtr; /* (in) TcpState structure */
|
|||
|
int option; /* (in) Option to set */
|
|||
|
int *valuePtr; /* (out) current value of option */
|
|||
|
{
|
|||
|
int sock, result, len, len1;
|
|||
|
struct linger l;
|
|||
|
|
|||
|
*valuePtr = 0;
|
|||
|
sock = statePtr->sock;
|
|||
|
len = sizeof(int);
|
|||
|
len1 = 1;
|
|||
|
|
|||
|
if (statePtr->myPort == 0) {
|
|||
|
result = DpSetAddress(statePtr);
|
|||
|
if (result != TCL_OK) {
|
|||
|
return DppGetErrno();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
switch (option) {
|
|||
|
case DP_KEEP_ALIVE:
|
|||
|
result = getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)valuePtr,
|
|||
|
&len);
|
|||
|
break;
|
|||
|
case DP_LINGER:
|
|||
|
len = sizeof(struct linger);
|
|||
|
result = getsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&l, &len);
|
|||
|
*valuePtr = l.l_onoff;
|
|||
|
break;
|
|||
|
case DP_RECV_BUFFER_SIZE:
|
|||
|
result = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)valuePtr,
|
|||
|
&len);
|
|||
|
break;
|
|||
|
case DP_REUSEADDR:
|
|||
|
result = getsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)valuePtr,
|
|||
|
&len);
|
|||
|
break;
|
|||
|
case DP_SEND_BUFFER_SIZE:
|
|||
|
result = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)valuePtr,
|
|||
|
&len);
|
|||
|
break;
|
|||
|
case DP_MYPORT:
|
|||
|
*valuePtr = statePtr->myPort;
|
|||
|
return 0;
|
|||
|
case DP_REMOTEPORT:
|
|||
|
*valuePtr = statePtr->destPort;
|
|||
|
return 0;
|
|||
|
case DP_MYIPADDR:
|
|||
|
*valuePtr = statePtr->myIpAddr;
|
|||
|
return 0;
|
|||
|
case DP_REMOTEIPADDR:
|
|||
|
*valuePtr = statePtr->destIpAddr;
|
|||
|
return 0;
|
|||
|
default:
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
if (result != 0) {
|
|||
|
return DppGetErrno();
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* DpSetAddress --
|
|||
|
*
|
|||
|
* Sets the local and remote address info of a socket.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* TCL_OK or TCL_ERROR.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
#ifndef MAXHOSTNAMELEN
|
|||
|
#define MAXHOSTNAMELEN 64
|
|||
|
#endif
|
|||
|
|
|||
|
static int
|
|||
|
DpSetAddress(statePtr)
|
|||
|
TcpState *statePtr;
|
|||
|
{
|
|||
|
struct sockaddr_in destSockAddr;
|
|||
|
struct sockaddr_in mySockAddr;
|
|||
|
struct hostent *h;
|
|||
|
int len;
|
|||
|
int *ipAddrPtr;
|
|||
|
char host[MAXHOSTNAMELEN];
|
|||
|
|
|||
|
/*
|
|||
|
* Set the destination port and addr only if
|
|||
|
* we are not a server socket. They are set to
|
|||
|
* zero in CreateServerChannel otherwise.
|
|||
|
*/
|
|||
|
len = sizeof(struct sockaddr_in);
|
|||
|
if (!(statePtr->flags & IS_SERVER)) {
|
|||
|
if (getpeername(statePtr->sock, (struct sockaddr *)&destSockAddr,
|
|||
|
&len) != 0) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
statePtr->destIpAddr = ntohl(destSockAddr.sin_addr.s_addr);
|
|||
|
statePtr->destPort = ntohs((unsigned short)destSockAddr.sin_port);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Set the local port and IP address
|
|||
|
*/
|
|||
|
len = sizeof(struct sockaddr_in);
|
|||
|
if (getsockname(statePtr->sock, (struct sockaddr *)&mySockAddr,
|
|||
|
&len) != 0) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (gethostname(host, sizeof(host)) != 0) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
h = gethostbyname(host);
|
|||
|
if (h == NULL) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
ipAddrPtr = &(statePtr->myIpAddr);
|
|||
|
memcpy ((char *)ipAddrPtr, (char *) h->h_addr_list[0],
|
|||
|
(size_t) h->h_length);
|
|||
|
*ipAddrPtr = ntohl(*ipAddrPtr);
|
|||
|
statePtr->myPort = ntohs((unsigned short)mySockAddr.sin_port);
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|