/* * 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; iflags & 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 { } */ 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; }