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;
|
||
}
|
||
|