886 lines
24 KiB
C
886 lines
24 KiB
C
/*
|
||
* generic/dpUdp.c --
|
||
*
|
||
* This file implements the generic code for a udp channel driver. These
|
||
* are channels that are created by evaluating "dp_connect udp".
|
||
*
|
||
* The architecture consists of a generic layer and a platform specific
|
||
* layer. The rational is that platform specific code goes in its layer,
|
||
* while platform independent code goes in its layer. However, most
|
||
* socket implementations use the Berkeley sockets interface, which is
|
||
* similar across platforms with a few annoying differences. These are
|
||
* separated into two files, dpSockUdp.c contains the generic socket code,
|
||
* which makes calls on routines in win/dpSock.c, which contains the
|
||
* platform specific code. We retain the two level architecture, though,
|
||
* so non-Berkeley socket interfaces can be built (if any still exist).
|
||
*
|
||
* 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"
|
||
|
||
/*
|
||
* Below are all the channel driver procedures that must be supplied for
|
||
* a channel. Replace Udp with the name of this channel type.
|
||
* In many cases, the DpGeneric driver procedures can be used (e.g.,
|
||
* "DpGenericBlockMode)"
|
||
*/
|
||
|
||
static int UdpBlockMode _ANSI_ARGS_((ClientData instanceData,
|
||
int mode));
|
||
static int UdpInput _ANSI_ARGS_((ClientData instanceData,
|
||
char *buf, int bufSize,
|
||
int *errorCodePtr));
|
||
static int UdpOutput _ANSI_ARGS_((ClientData instanceData,
|
||
char *buf, int toWrite,
|
||
int *errorCodePtr));
|
||
static int UdpClose _ANSI_ARGS_((ClientData instanceData,
|
||
Tcl_Interp *interp));
|
||
static int UdpSetOption _ANSI_ARGS_((ClientData instanceData,
|
||
Tcl_Interp *interp, char *optionName,
|
||
char *optionValue));
|
||
static int UdpGetOption _ANSI_ARGS_((ClientData instanceData,
|
||
char *optionName, Tcl_DString *dsPtr));
|
||
|
||
static void UdpWatch _ANSI_ARGS_((ClientData instanceData,
|
||
int mask));
|
||
static int UdpReady _ANSI_ARGS_((ClientData instanceData,
|
||
int mask));
|
||
|
||
static Tcl_File UdpGetFile _ANSI_ARGS_((ClientData instanceData,
|
||
int direction));
|
||
|
||
typedef SocketState UdpState;
|
||
|
||
static Tcl_ChannelType udpChannelType = {
|
||
"udp", /* Name of channel */
|
||
UdpBlockMode, /* Proc to set blocking mode on socket */
|
||
UdpClose, /* Proc to close a socket */
|
||
UdpInput, /* Proc to get input from a socket */
|
||
UdpOutput, /* Proc to send output to a socket */
|
||
NULL, /* Can't seek on a socket! */
|
||
UdpSetOption, /* Proc to set a socket option */
|
||
UdpGetOption, /* Proc to set a socket option */
|
||
UdpWatch, /* Proc called to set event loop wait params */
|
||
UdpReady, /* Proc called to check if socket has input */
|
||
UdpGetFile /* Proc to return a handle assoc with socket */
|
||
};
|
||
|
||
#define PEEK_MODE (1<<1) /* Read without consuming? */
|
||
|
||
static int udpCount = 0; /* Number of udp files opened -- used to
|
||
* generate unique ids for channels */
|
||
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* UdpBlockMode --
|
||
*
|
||
* Sets the udp 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
|
||
UdpBlockMode (instanceData, mode)
|
||
ClientData instanceData; /* Pointer to udpState struct */
|
||
int mode; /* TCL_MODE_BLOCKING or TCL_MODE_NONBLOCKING */
|
||
{
|
||
if (mode == TCL_MODE_BLOCKING) {
|
||
return DpUdpSetSocketOption(instanceData, DP_BLOCK, 1);
|
||
} else {
|
||
return DpUdpSetSocketOption(instanceData, DP_BLOCK, 0);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* UdpClose --
|
||
*
|
||
* 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
|
||
UdpClose (instanceData, interp)
|
||
ClientData instanceData; /* (in) Pointer to udpState struct */
|
||
Tcl_Interp *interp; /* (in) For error reporting */
|
||
{
|
||
UdpState *statePtr = (UdpState *)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;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* UdpInput --
|
||
*
|
||
* This function is called by the Tcl channel driver whenever
|
||
* the user wants to get input from the UDP 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 -1 in case of error (with errorCodePtr set to the POSIX
|
||
* error code).
|
||
*
|
||
* Side effects:
|
||
* None
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
static int
|
||
UdpInput (instanceData, buf, bufSize, errorCodePtr)
|
||
ClientData instanceData; /* (in) Pointer to udpState struct */
|
||
char *buf; /* (in/out) Buffer to fill */
|
||
int bufSize; /* (in) Size of buffer */
|
||
int *errorCodePtr; /* (out) POSIX error code (if any) */
|
||
{
|
||
UdpState *statePtr = (UdpState *)instanceData;
|
||
int result, peek;
|
||
int fromHost, fromPort;
|
||
char str[256];
|
||
DpSocketAddressIP fromAddr;
|
||
int bytesRead, flags = 0, fromLen;
|
||
|
||
peek = (statePtr->flags & PEEK_MODE);
|
||
if (peek) {
|
||
flags = MSG_PEEK;
|
||
} else {
|
||
flags = 0;
|
||
}
|
||
fromLen = sizeof(fromAddr);
|
||
bytesRead = recvfrom(statePtr->sock, buf, bufSize, flags,
|
||
(DpSocketAddress *)&fromAddr, &fromLen);
|
||
if (bytesRead == DP_SOCKET_ERROR) {
|
||
*errorCodePtr = DppGetErrno();
|
||
return -1;
|
||
}
|
||
if (statePtr->interp != NULL) {
|
||
fromHost = ntohl(fromAddr.sin_addr.s_addr);
|
||
fromPort = ntohs(fromAddr.sin_port);
|
||
|
||
sprintf (str, "{%d.%d.%d.%d %d}", (fromHost>>24),
|
||
(fromHost>>16) & 0xFF, (fromHost>>8) & 0xFF, fromHost & 0xFF,
|
||
fromPort);
|
||
Tcl_SetVar(statePtr->interp, "dp_from", str, TCL_GLOBAL_ONLY);
|
||
}
|
||
return bytesRead;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* UdpOutput --
|
||
*
|
||
* This function is called by the Tcl channel driver whenever
|
||
* the user wants to send output to the UDP socket.
|
||
* The function writes toWrite bytes from buf to the socket.
|
||
*
|
||
* Results:
|
||
* 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.
|
||
*
|
||
* Side effects:
|
||
* None
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
static int
|
||
UdpOutput (instanceData, buf, toWrite, errorCodePtr)
|
||
ClientData instanceData; /* (in) Pointer to udpState struct */
|
||
char *buf; /* (in) Buffer to write */
|
||
int toWrite; /* (in) Number of bytes to write */
|
||
int *errorCodePtr; /* (out) POSIX error code (if any) */
|
||
{
|
||
UdpState *statePtr = (UdpState *) instanceData;
|
||
DpSocketAddressIP dsa;
|
||
int result;
|
||
|
||
dsa.sin_family = AF_INET;
|
||
dsa.sin_addr.s_addr = htonl(statePtr->destIpAddr);
|
||
dsa.sin_port = htons((unsigned short)statePtr->destPort);
|
||
|
||
result = sendto(statePtr->sock, buf, toWrite, 0,
|
||
(DpSocketAddress *)&dsa, sizeof(dsa));
|
||
if (result == DP_SOCKET_ERROR) {
|
||
*errorCodePtr = DppGetErrno();
|
||
}
|
||
return result;
|
||
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* UdpSetOption --
|
||
*
|
||
* This function is called by the Tcl channel driver
|
||
* whenever Tcl evaluates and fconfigure call to set
|
||
* some property of the udp 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
|
||
UdpSetOption (instanceData, interp, optionName, optionValue)
|
||
ClientData instanceData;
|
||
Tcl_Interp *interp;
|
||
char *optionName;
|
||
char *optionValue;
|
||
{
|
||
int option;
|
||
int value;
|
||
UdpState *statePtr = (UdpState *)instanceData;
|
||
|
||
/*
|
||
* Set the option specified by optionName
|
||
*/
|
||
if (optionName[0] == '-') {
|
||
option = DpTranslateOption(optionName+1);
|
||
} else {
|
||
option = -1;
|
||
}
|
||
switch (option) {
|
||
case DP_SEND_BUFFER_SIZE:
|
||
case DP_RECV_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 DpUdpSetSocketOption (statePtr, option, value);
|
||
|
||
case DP_PEEK:
|
||
if (Tcl_GetBoolean(interp, optionValue, &value) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
if (value == 0) {
|
||
statePtr->flags &= ~PEEK_MODE;
|
||
} else {
|
||
statePtr->flags |= PEEK_MODE;
|
||
}
|
||
break;
|
||
|
||
case DP_HOST:
|
||
if (DpHostToIpAddr (optionValue, &value) == 0) {
|
||
Tcl_AppendResult (interp,
|
||
"Expected IP address or hostname but got \"",
|
||
optionValue, "\"", NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
statePtr->destIpAddr = value;
|
||
break;
|
||
|
||
case DP_PORT:
|
||
if (Tcl_GetInt(interp, optionValue, &value) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
if (value <= 0) {
|
||
Tcl_AppendResult (interp, "Port number must be > 0", NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
statePtr->destPort = (unsigned short) value;
|
||
break;
|
||
|
||
case DP_MYPORT:
|
||
Tcl_AppendResult (interp, "Can't set port after socket is opened",
|
||
NULL);
|
||
return TCL_ERROR;
|
||
|
||
default:
|
||
Tcl_AppendResult (interp, "Illegal option \"", optionName,
|
||
"\" -- must be sendBuffer, recvBuffer, peek, ",
|
||
"host, port, or a standard fconfigure option", NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* UdpGetOption --
|
||
*
|
||
* 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
|
||
UdpGetOption (instanceData, optionName, dsPtr)
|
||
ClientData instanceData;
|
||
char *optionName;
|
||
Tcl_DString *dsPtr;
|
||
{
|
||
int option;
|
||
int size;
|
||
unsigned int addr;
|
||
char str[256];
|
||
UdpState *statePtr = (UdpState *)instanceData;
|
||
|
||
/*
|
||
* If optionName is NULL, then store an alternating list of
|
||
* all supported options and their current values in dsPtr
|
||
*/
|
||
if (optionName == NULL) {
|
||
Tcl_DStringAppend (dsPtr, " -sendBuffer ", -1);
|
||
UdpGetOption(instanceData, "-sendBuffer", dsPtr);
|
||
Tcl_DStringAppend (dsPtr, " -recvBuffer ", -1);
|
||
UdpGetOption(instanceData, "-recvBuffer", dsPtr);
|
||
Tcl_DStringAppend (dsPtr, " -peek ", -1);
|
||
UdpGetOption(instanceData, "-peek", dsPtr);
|
||
Tcl_DStringAppend (dsPtr, " -host ", -1);
|
||
UdpGetOption(instanceData, "-host", dsPtr);
|
||
Tcl_DStringAppend (dsPtr, " -port ", -1);
|
||
UdpGetOption(instanceData, "-port", dsPtr);
|
||
Tcl_DStringAppend (dsPtr, " -myport ", -1);
|
||
UdpGetOption(instanceData, "-myport", 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_SEND_BUFFER_SIZE:
|
||
case DP_RECV_BUFFER_SIZE:
|
||
DpUdpGetSocketOption (statePtr, option, &size);
|
||
sprintf (str, "%d", size);
|
||
Tcl_DStringAppend (dsPtr, str, -1);
|
||
break;
|
||
|
||
case DP_PEEK:
|
||
if (statePtr->flags & PEEK_MODE) {
|
||
Tcl_DStringAppend (dsPtr, "1", -1);
|
||
} else {
|
||
Tcl_DStringAppend (dsPtr, "0", -1);
|
||
}
|
||
break;
|
||
|
||
case DP_HOST:
|
||
addr = statePtr->destIpAddr;
|
||
sprintf (str, "%d.%d.%d.%d",
|
||
(addr >>24), (addr >>16) & 0xff,
|
||
(addr >> 8) & 0xff, (addr) & 0xff);
|
||
Tcl_DStringAppend (dsPtr, str, -1);
|
||
break;
|
||
|
||
case DP_PORT:
|
||
sprintf (str, "%d", (unsigned short) statePtr->destPort);
|
||
Tcl_DStringAppend (dsPtr, str, -1);
|
||
break;
|
||
|
||
case DP_MYPORT:
|
||
sprintf (str, "%d", (unsigned short) statePtr->myPort);
|
||
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;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* DpOpenUdpChannel --
|
||
*
|
||
* Opens a new channel that uses the UDP 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
|
||
DpOpenUdpChannel(interp, argc, argv)
|
||
Tcl_Interp *interp; /* For error reporting; can be NULL. */
|
||
int argc; /* Number of arguments. */
|
||
char **argv; /* Argument strings. */
|
||
{
|
||
Tcl_Channel chan;
|
||
UdpState *statePtr;
|
||
char channelName[20];
|
||
int i, result;
|
||
|
||
/*
|
||
* The default values for the value-option pairs
|
||
*/
|
||
|
||
int hostIp = 0;
|
||
int port = 0;
|
||
int myIpAddr = DP_INADDR_ANY;
|
||
int myport = 0;
|
||
|
||
for (i=0; i<argc; i+=2) {
|
||
int v = i+1;
|
||
size_t len = strlen(argv[i]);
|
||
|
||
if (strncmp(argv[i], "-host", len)==0) {
|
||
if (v==argc) {goto arg_missing;}
|
||
if (!DpHostToIpAddr (argv[v], &hostIp)) {
|
||
Tcl_AppendResult (interp, "Unknown host \"", argv[v],
|
||
"\"", NULL);
|
||
return NULL;
|
||
}
|
||
} else if (strncmp(argv[i], "-port", len)==0) {
|
||
if (v==argc) {goto arg_missing;}
|
||
if (Tcl_GetInt(interp, argv[v], &port) != TCL_OK) {
|
||
return NULL;
|
||
}
|
||
if (port <= 0) {
|
||
Tcl_AppendResult (interp, "Port number must be > 0", NULL);
|
||
return NULL;
|
||
}
|
||
} 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;
|
||
}
|
||
if (myport <= 0) {
|
||
Tcl_AppendResult (interp,
|
||
"Port number for -myport must be > 0", NULL);
|
||
return NULL;
|
||
}
|
||
} else {
|
||
Tcl_AppendResult(interp, "unknown option \"",
|
||
argv[i], "\", must be -host, -myaddr, -myport ",
|
||
"or -port", NULL);
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Create a new socket and wrap it in a channel.
|
||
*/
|
||
|
||
statePtr = (UdpState *)ckalloc(sizeof(UdpState));
|
||
statePtr->flags = 0;
|
||
statePtr->interp = interp;
|
||
statePtr->myPort = myport;
|
||
statePtr->destIpAddr = hostIp;
|
||
statePtr->destPort = port;
|
||
result = DpCreateUdpSocket(interp, myIpAddr, statePtr);
|
||
|
||
if (result != TCL_OK) {
|
||
ckfree((char *)statePtr);
|
||
return NULL;
|
||
}
|
||
sprintf(channelName, "udp%d", udpCount++);
|
||
chan = Tcl_CreateChannel(&udpChannelType, channelName,
|
||
(ClientData)statePtr, TCL_READABLE|TCL_WRITABLE);
|
||
Tcl_RegisterChannel(interp, chan);
|
||
|
||
/*
|
||
* Set the initial state of the channel.
|
||
* Make sure the socket's blocking, set the default buffer sizes,
|
||
* set the destination address as specified, disable Tcl buffering
|
||
* and translation.
|
||
*/
|
||
|
||
DpUdpSetSocketOption(statePtr, DP_SEND_BUFFER_SIZE, 8192);
|
||
DpUdpSetSocketOption(statePtr, DP_RECV_BUFFER_SIZE, 8192);
|
||
|
||
DpUdpGetSocketOption(statePtr, DP_RECV_BUFFER_SIZE,
|
||
&statePtr->recvBufSize);
|
||
|
||
if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") !=
|
||
TCL_OK) {
|
||
DpClose(interp, chan);
|
||
ckfree((char *)statePtr);
|
||
return NULL;
|
||
}
|
||
if (Tcl_SetChannelOption(interp, chan, "-blocking", "1") !=
|
||
TCL_OK) {
|
||
DpClose(interp, chan);
|
||
ckfree((char *)statePtr);
|
||
return NULL;
|
||
}
|
||
if (Tcl_SetChannelOption(interp, chan, "-buffering", "none") !=
|
||
TCL_OK) {
|
||
DpClose(interp, chan);
|
||
ckfree((char *)statePtr);
|
||
return NULL;
|
||
}
|
||
|
||
return chan;
|
||
|
||
arg_missing:
|
||
Tcl_AppendResult(interp, "value for \"", argv[argc-1], "\" missing", NULL);
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* UdpWatch --
|
||
*
|
||
* 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:
|
||
* Description of return values.
|
||
*
|
||
* Side effects:
|
||
* Global variables touched.
|
||
* I/O operations performed.
|
||
* Delayed effects.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
static void
|
||
UdpWatch (instanceData, mask)
|
||
ClientData instanceData;
|
||
int mask;
|
||
{
|
||
UdpState *infoPtr = (UdpState *) instanceData;
|
||
|
||
Tcl_WatchFile(infoPtr->sockFile, mask);
|
||
}
|
||
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* UdpReady --
|
||
*
|
||
* 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:
|
||
* Description of return values.
|
||
*
|
||
* Side effects:
|
||
* Global variables touched.
|
||
* I/O operations performed.
|
||
* Delayed effects.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
static int
|
||
UdpReady (instanceData, mask)
|
||
ClientData instanceData;
|
||
int mask;
|
||
{
|
||
UdpState *statePtr = (UdpState *) instanceData;
|
||
return Tcl_FileReady(statePtr->sockFile, mask);
|
||
}
|
||
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* UdpGetFile --
|
||
*
|
||
* 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:
|
||
* Description of return values.
|
||
*
|
||
* Side effects:
|
||
* Global variables touched.
|
||
* I/O operations performed.
|
||
* Delayed effects.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
static Tcl_File
|
||
UdpGetFile(instanceData, direction)
|
||
ClientData instanceData;
|
||
int direction;
|
||
{
|
||
UdpState *statePtr = (UdpState *)instanceData;
|
||
|
||
return statePtr->sockFile;
|
||
}
|
||
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* DpUdpSetSocketOption --
|
||
*
|
||
* Sets a socket option. The allowable options for UDP
|
||
* sockets are
|
||
* DP_SEND_BUFFER_SIZE (int)
|
||
* DP_RECV_BUFFER_SIZE (int)
|
||
* DP_BLOCK (T/F)
|
||
*
|
||
* Results:
|
||
* Zero if the operation was successful, or a nonzero POSIX
|
||
* error code if the operation failed.
|
||
*
|
||
* Side effects:
|
||
* None
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
int
|
||
DpUdpSetSocketOption (clientData, option, value)
|
||
ClientData clientData; /* (in) UdpState structure */
|
||
int option; /* (in) Option to set */
|
||
int value; /* (in) new value for option */
|
||
{
|
||
UdpState *statePtr = (UdpState *)clientData;
|
||
int sock, result;
|
||
|
||
sock = statePtr->sock;
|
||
switch (option) {
|
||
case DP_SEND_BUFFER_SIZE:
|
||
result = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&value,
|
||
sizeof(value));
|
||
break;
|
||
|
||
case DP_RECV_BUFFER_SIZE:
|
||
result = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&value,
|
||
sizeof(value));
|
||
break;
|
||
|
||
case DP_BLOCK:
|
||
result = DppSetBlock (sock, value);
|
||
break;
|
||
|
||
default:
|
||
return EINVAL;
|
||
}
|
||
|
||
if (result != 0) {
|
||
return Tcl_GetErrno();
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* DpUdpGetSocketOption --
|
||
*
|
||
* Sets a socket option. The allowable options for UDP
|
||
* 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
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
int
|
||
DpUdpGetSocketOption (clientData, option, valuePtr)
|
||
ClientData clientData; /* (in) UdpState structure */
|
||
int option; /* (in) Option to set */
|
||
int *valuePtr; /* (out) current value of option */
|
||
{
|
||
UdpState *statePtr = (UdpState *)clientData;
|
||
int sock, result, len;
|
||
|
||
sock = statePtr->sock;
|
||
len = sizeof(int);
|
||
switch (option) {
|
||
case DP_SEND_BUFFER_SIZE:
|
||
result = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)valuePtr,
|
||
&len);
|
||
break;
|
||
|
||
case DP_RECV_BUFFER_SIZE:
|
||
result = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)valuePtr,
|
||
&len);
|
||
break;
|
||
|
||
default:
|
||
return EINVAL;
|
||
}
|
||
|
||
if (result != 0) {
|
||
return Tcl_GetErrno();
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* DpCreateUdpSocket --
|
||
*
|
||
* Create a udp socket.
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* None
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
int
|
||
DpCreateUdpSocket(interp, myIpAddr, statePtr)
|
||
Tcl_Interp *interp; /* (in) For error reporting. */
|
||
int myIpAddr; /* (in) IP addr of interface to use.
|
||
* DP_INADDR_ANY = default port */
|
||
UdpState *statePtr; /* (out) Pointer to local structure */
|
||
{
|
||
DpSocketAddressIP sockAddr;
|
||
DpSocket sock;
|
||
int len;
|
||
|
||
sock = socket(PF_INET, SOCK_DGRAM, 0);
|
||
if (sock == DP_SOCKET_ERROR) {
|
||
goto socketError;
|
||
}
|
||
statePtr->sock = sock;
|
||
|
||
/*
|
||
* Bind the socket.
|
||
* This is a bit of a mess, but it's Berkeley sockets. The sin_family
|
||
* is set to AF_INET, indicating IP addressing. The sin_addr.s_addr
|
||
* field says what interface to use. It can be INADDR_ANY to let
|
||
* the system choose a default interface. The port number can be
|
||
* zero (which tells the system to choose a port number) or > 1024,
|
||
* which is then used as the port number
|
||
*/
|
||
|
||
memset((char *)&sockAddr, 0, sizeof(sockAddr));
|
||
sockAddr.sin_family = AF_INET;
|
||
if (myIpAddr == DP_INADDR_ANY) {
|
||
sockAddr.sin_addr.s_addr = INADDR_ANY;
|
||
} else {
|
||
sockAddr.sin_addr.s_addr = htonl(myIpAddr);
|
||
}
|
||
sockAddr.sin_port = htons((unsigned short) statePtr->myPort);
|
||
if (bind(sock, (DpSocketAddress *)&sockAddr, sizeof(sockAddr)) ==
|
||
DP_SOCKET_ERROR) {
|
||
goto bindError;
|
||
}
|
||
|
||
/*
|
||
* Figure out what port number we got if we let the system chose it.
|
||
*/
|
||
|
||
if (statePtr->myPort == 0) {
|
||
len = sizeof(sockAddr);
|
||
getsockname (sock, (DpSocketAddress *)&sockAddr, &len);
|
||
statePtr->myPort = ntohs(sockAddr.sin_port);
|
||
}
|
||
statePtr->sockFile = Tcl_GetFile((ClientData)statePtr->sock, DP_SOCKET);
|
||
|
||
return TCL_OK;
|
||
|
||
bindError:
|
||
DppGetErrno();
|
||
Tcl_AppendResult(interp, "Error binding UDP socket to port: ",
|
||
Tcl_PosixError(interp), NULL);
|
||
DppCloseSocket (sock);
|
||
return TCL_ERROR;
|
||
|
||
socketError:
|
||
DppGetErrno();
|
||
Tcl_AppendResult(interp, "Error creating UDP socket: ",
|
||
Tcl_PosixError(interp), NULL);
|
||
return TCL_ERROR;
|
||
}
|