/* * network.c -- * * This file implements most of the network connection management * functions of Tcl-DP. The following comments are inherited from * the progenitors of Tcl-DP. * * This file contains a simple Tcl "connect" command * that returns an standard Tcl File descriptor (as would * be returned by Tcl_OpenCmd). This part of the file was written by * Pekka Nikander * * Tim MacKenzie extended it to * create servers, accept connections, shutdown parts of full * duplex connections and handle UNIX domain sockets. * * Brian Smith further modified it to * add support for various send/receive primitives, and connectionless * sockets. * * Copyright 1992 Telecom Finland * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that this copyright * notice appears in all copies. Telecom Finland * makes no representations about the suitability of this * software for any purpose. It is provided "as is" without * express or implied warranty. * * Copyright 1992 Regents of the University of California. * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies. The University of California * makes no representations about the suitability of this * software for any purpose. It is provided "as is" without * express or implied warranty. * */ #include "tclInt.h" #include "tclUnix.h" #include #include #include #include #include #include #include #include #include #include "default.h" #include "tclInt.h" #include "tkInt.h" #include "tk.h" #include "network.h" #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define abs(a) ((a)>0?(a):-(a)) /* * This is a "magic number" prepended to the beginning of the packet * It's used to help resync the packet machanism in the event of errors. */ #define PACKET_MAGIC 0x6feeddcc static int inet_connect _ANSI_ARGS_((char *host, int port, int server, int udp)); static int unix_connect _ANSI_ARGS_((char *path, int server, int udp)); static void HandleEvent _ANSI_ARGS_((ClientData clientData, int mask)); /* * For every file descriptor handler created, a structure of * the following type is maintained. */ typedef struct FileHandle { Tcl_Interp *interp; OpenFile *filePtr; /* Open file descriptor (file or socket) */ int mask; /* Mask of file descriptor conditions */ char *rCmd; /* Command to call on readable condition */ char *wCmd; /* Command to call on writable condition */ char *eCmd; /* Command to call on exception condition */ char *fileId; /* Represents filePtr */ } FileHandle; static FileHandle *handlers[256]; /* Indexed by fd. */ /* *------------------------------------------------------------------ * * Tcp_MakeOpenFile -- * * Set up an OpenFile structure in the interpreter. * * Results: * None * * Side effects: * Adds an OpenFile to the list. *------------------------------------------------------------------ */ /* ARGSUSED */ static void Tcp_MakeOpenFile (interp, fd) Tcl_Interp *interp; int fd; { Interp *interpPtr = (Interp *) interp; OpenFile *filePtr; filePtr = (OpenFile *) ckalloc (sizeof (OpenFile)); filePtr->f = NULL; filePtr->f2 = NULL; /* * Open the file with the correct type */ filePtr->f = fdopen (fd, "r+"); /* * Don't do buffered communication if full-duplex... it breaks! */ setbuf (filePtr->f, (char *) NULL); filePtr->readable = 1; filePtr->writable = 1; filePtr->numPids = 0; filePtr->pidPtr = NULL; filePtr->errorId = -1; /* * Enter this new OpenFile structure in the table for the interpreter. * May have to expand the table to do this. */ TclMakeFileTable (interpPtr, fd); if (interpPtr->filePtrArray[fd] != NULL) { panic("Tcl_OpenCmd found file already open"); } interpPtr->filePtrArray[fd] = filePtr; } /* *------------------------------------------------------------------ * * Tcp_ConnectCmd -- * * Open a socket and connect it. * * Results: * A standard Tcl result. * * Side effects: * An open socket connection. * *------------------------------------------------------------------ */ /* ARGSUSED */ int Tcp_ConnectCmd(notUsed, interp, argc, argv) ClientData notUsed; Tcl_Interp *interp; int argc; char **argv; { int fd; /* Open file descriptor */ int port; /* User specified port number */ char *pathname, *host; /* Pathname (unix) or host (inet) */ int unixSocket; /* Unix domain socket? */ int udp; /* UDP protocol? */ int server; /* Set up listening socket? */ udp = 0; server = 0; unixSocket = 0; switch (argc) { case 4: /* Must be "connect -server host port" */ if (strcmp(argv[1], "-server") != 0) { sprintf(interp->result, "%s: should be \"connect -server host port\"", argv[0]); return TCL_ERROR; } server = 1; host = argv[2]; if (Tcl_GetInt(interp, argv[3], &port) != 0) { return TCL_ERROR; } break; case 3: /* Either "connect host port", * "connect -udp port", or * "connect -server path" */ if (strcmp(argv[1], "-server") == 0) { pathname = argv[2]; unixSocket = 1; server = 1; } else if (strcmp(argv[1], "-udp") == 0) { host = ""; if (Tcl_GetInt(interp, argv[2], &port) != 0) { return TCL_ERROR; } udp = 1; } else { host = argv[1]; if (Tcl_GetInt(interp, argv[2], &port) != 0) { return TCL_ERROR; } } break; case 2: /* Must be "connect path" */ pathname = argv[1]; unixSocket = 1; break; default: sprintf(interp->result, "%s: should be one of the forms:\n", argv[0]); Tcl_AppendResult(interp, " \"connect -server host port\"\n", (char *) NULL); Tcl_AppendResult(interp, " \"connect host port\"\n", (char *) NULL); Tcl_AppendResult(interp, " \"connect -udp port\"\n", (char *) NULL); Tcl_AppendResult(interp, " \"connect -server path\"\n", (char *) NULL); Tcl_AppendResult(interp, " or \"connect path\"\n", (char *) NULL); return TCL_ERROR; } /* * Create the connection */ if (unixSocket) { fd = unix_connect(pathname, server, udp); } else { fd = inet_connect(host, port, server, udp); } if (fd < 0) { /* Tell them why it fell apart */ if (unixSocket) { if (server) { sprintf(interp->result, "Couldn't setup listening socket with path \"%s\": ", pathname, Tcl_UnixError(interp)); } else { sprintf(interp->result, "Couldn't connect to \"%s\": ", pathname, Tcl_UnixError(interp)); } } else if (server) { if (port == 0) { sprintf(interp->result, "Couldn't setup listening socket on any port: %s", Tcl_UnixError(interp)); } else { sprintf(interp->result, "Couldn't setup listening socket on port %d: %s", port, Tcl_UnixError(interp)); } } else { sprintf(interp->result, "Couldn't open connection to %s:%d : %s", host, port, Tcl_UnixError(interp)); } return TCL_ERROR; } if (!unixSocket) { struct sockaddr_in sockaddr; int res, len; /* Find the local port we're using for the connection. */ len = sizeof (sockaddr); res = getsockname (fd, (struct sockaddr *) &sockaddr, &len); if (res < 0) { sprintf(interp->result, "file%d %d", fd, errno); } else sprintf(interp->result, "file%d %d", fd, (int) ntohs(sockaddr.sin_port)); } else { sprintf(interp->result, "file%d", fd); } Tcp_MakeOpenFile(interp, fd); return TCL_OK; } /* *-------------------------------------------------------------- * * FindFileHandler -- * * Find the filehandler associated with the * descriptor passed in. * * Results: * A pointer to the handler, or NULL if there is none. * * Side effects: * None. * *-------------------------------------------------------------- */ static FileHandle * FindFileHandler (filePtr) OpenFile *filePtr; { int fd; if (!filePtr) return ((FileHandle *) NULL); fd = fileno (filePtr->f); if ((fd < 0) || (fd > 255)) return ((FileHandle *) NULL); return (handlers[fd]); } /* *------------------------------------------------------------------ * * Tcp_ShutdownCmd -- * * Shutdown a socket for reading writing or both using * the shutdown (2) system call. * * Results: * A standard Tcl result. * * Side effects: * Modifies the OpenFile structure appropriately. * Delete any created filehandlers. * *------------------------------------------------------------------ */ /* ARGSUSED */ int Tcp_ShutdownCmd(notUsed, interp, argc, argv) ClientData notUsed; Tcl_Interp *interp; int argc; char **argv; { OpenFile *filePtr; int fd; FileHandle *handler; /* * Check args, find file */ if (argc != 3) { wrong_args: Tcl_AppendResult (interp, "wrong # args: should be \"", argv[0], " fileid