# include <Files.h> # include <Folders.h> # include <Errors.h> # include <Resources.h> # include <Memory.h> # include <Devices.h> # include <MacTCP.h> # include <OSUtils.h> # include "dgd.h" # include "str.h" # include "array.h" # include "object.h" # include "data.h" # include "comm.h" # define OPENRESOLVER 1L # define CLOSERESOLVER 2L # define ADDRTONAME 6L # define NUM_ALT_ADDRS 4 struct hostInfo { int rtnCode; /* call return code */ char cname[255]; /* host name */ unsigned long addr[NUM_ALT_ADDRS]; /* addresses */ }; typedef pascal void (*ResultProcPtr)(struct hostInfo *host, char *data); typedef OSErr (*dnrfunc)(long, ...); static Handle code; /* DNR code resource */ static dnrfunc dnr; /* DNR function pointer */ /* * NAME: findcdev() * DESCRIPTION: find TCP/IP control panel with the given creator */ static short findcdev(long creator, short vref, long dirid) { HFileInfo buf; Str255 str; short rsrc; buf.ioNamePtr = str; buf.ioVRefNum = vref; buf.ioDirID = dirid; buf.ioFDirIndex = 1; while (PBGetCatInfoSync((CInfoPBPtr) &buf) == noErr) { if (buf.ioFlFndrInfo.fdType == 'cdev' && buf.ioFlFndrInfo.fdCreator == creator) { rsrc = HOpenResFile(vref, dirid, str, fsRdPerm); if (GetIndResource('dnrp', 1) == NULL) { CloseResFile(rsrc); /* failed */ } else { return rsrc; /* found TCP/IP cdev */ } } buf.ioFDirIndex++; buf.ioDirID = dirid; } return -1; } /* * NAME: opendnrp() * DESCRIPTION: open the 'dnrp' resource in the TCP/IP control panel */ static short opendnrp(void) { short rsrc; short vref; long dirid; /* search for MacTCP 1.1, 2.0.x */ FindFolder(kOnSystemDisk, kControlPanelFolderType, kDontCreateFolder, &vref, &dirid); rsrc = findcdev('ztcp', vref, dirid); if (rsrc >= 0) { return rsrc; } /* search for MacTCP 1.0.x */ FindFolder(kOnSystemDisk, kSystemFolderType, kDontCreateFolder, &vref, &dirid); rsrc = findcdev('mtcp', vref, dirid); if (rsrc >= 0) { return rsrc; } /* search for MacTCP 1.0.x */ FindFolder(kOnSystemDisk, kControlPanelFolderType, kDontCreateFolder, &vref, &dirid); rsrc = findcdev('mtcp', vref, dirid); if (rsrc >= 0) { return rsrc; } return -1; /* not found */ } /* * NAME: OpenResolver() * DESCRIPTION: MacTCP: open DNR */ static OSErr OpenResolver(char *filename) { short rsrc; OSErr result; if (dnr != NULL) { return noErr; /* already open */ } /* open 'dnrp' resource in TCP/IP control panel */ rsrc = opendnrp(); code = GetIndResource('dnrp', 1); if (code == NULL) { return ResError(); /* failed; rsrc < 0 */ } DetachResource(code); if (rsrc >= 0) { CloseResFile(rsrc); } HLock(code); dnr = (dnrfunc) *code; /* initialize DNR */ result = (*dnr)(OPENRESOLVER, filename); if (result != noErr) { /* init failed, unload DNR */ HUnlock(code); DisposeHandle(code); dnr = NULL; } return result; } /* * NAME: CloseResolver() * DESCRIPTION: MacTCP: close DNR */ static OSErr CloseResolver(void) { if (dnr == NULL) { return notOpenErr; } /* close & unload */ (*dnr)(CLOSERESOLVER); dnr = NULL; HUnlock(code); DisposeHandle(code); return noErr; } /* * NAME: AddrToName() * DESCRIPTION: MacTCP: gethostbyaddr() */ static OSErr AddrToName(unsigned long addr, struct hostInfo *host, ResultProcPtr report, char *data) { if (dnr == NULL) { return notOpenErr; } return (*dnr)(ADDRTONAME, addr, host, report, data); } # define MAXHOSTNAMELEN 256 # define NFREE 32 typedef struct _ipaddr_ { struct _ipaddr_ *link; /* next in hash table */ struct _ipaddr_ *prev; /* previous in linked list */ struct _ipaddr_ *next; /* next in linked list */ Uint ref; /* reference count */ unsigned long ipnum; /* ip number */ char name[MAXHOSTNAMELEN]; /* ip name */ } ipaddr; static ipaddr **ipahtab; /* ip address hash table */ static unsigned int ipahtabsz; /* hash table size */ static ipaddr *qhead, *qtail; /* request queue */ static ipaddr *ffirst, *flast; /* free list */ static int nfree; /* # in free list */ static ipaddr *lastreq; /* last request */ static struct hostInfo host; /* host name etc. */ static bool lookup, busy; /* name resolver activity */ /* * NAME: ipaddr->report() * DESCRIPTION: DNR reporting back */ static pascal void ipa_report(struct hostInfo *host, char *busy) { *busy = FALSE; } /* * NAME: ipaddr->init() * DESCRIPTION: initialize name lookup */ static bool ipa_init(int maxusers) { if (OpenResolver(NULL) != noErr) { return FALSE; } ipahtab = ALLOC(ipaddr*, ipahtabsz = maxusers); memset(ipahtab, '\0', ipahtabsz * sizeof(ipaddr*)); qhead = qtail = ffirst = flast = lastreq = (ipaddr *) NULL; nfree = 0; lookup = busy = FALSE; return TRUE; } /* * NAME: ipaddr->finish() * DESCRIPTION: stop name lookup */ static void ipa_finish(void) { CloseResolver(); } /* * NAME: ipaddr->new() * DESCRIPTION: return a new ipaddr */ static ipaddr *ipa_new(unsigned long ipnum) { register ipaddr *ipa, **hash; /* check hash table */ hash = &ipahtab[ipnum % ipahtabsz]; while (*hash != (ipaddr *) NULL) { ipa = *hash; if (ipnum == ipa->ipnum) { /* * found it */ if (ipa->ref == 0) { /* remove from free list */ if (ipa->prev == (ipaddr *) NULL) { ffirst = ipa->next; } else { ipa->prev->next = ipa->next; } if (ipa->next == (ipaddr *) NULL) { flast = ipa->prev; } else { ipa->next->prev = ipa->prev; } ipa->prev = ipa->next = (ipaddr *) NULL; --nfree; } ipa->ref++; return ipa; } hash = &ipa->link; } if (nfree >= NFREE && ffirst != (ipaddr *) NULL) { register ipaddr **h; /* * use first ipaddr in free list */ ipa = ffirst; ffirst = ipa->next; if (ffirst == (ipaddr *) NULL) { flast = (ipaddr *) NULL; } --nfree; /* remove from hash table */ for (h = &ipahtab[ipa->ipnum % ipahtabsz]; *h != ipa; h = &(*h)->link) ; *h = ipa->next; if (ipa == lastreq) { lastreq = (ipaddr *) NULL; } } else { /* * allocate new ipaddr */ m_static(); ipa = ALLOC(ipaddr, 1); m_dynamic(); } /* put in hash table */ ipa->link = *hash; *hash = ipa; ipa->ref++; ipa->ipnum = ipnum; ipa->name[0] = '\0'; if (!busy) { /* send query to name resolver */ host.cname[0] = '\0'; lookup = busy = TRUE; if (AddrToName(ipnum, &host, ipa_report, &busy) != cacheFault) { busy = FALSE; } ipa->prev = ipa->next = (ipaddr *) NULL; lastreq = ipa; } else { /* put in request queue */ ipa->prev = qtail; if (qtail == (ipaddr *) NULL) { qhead = ipa; } qtail = ipa; ipa->next = (ipaddr *) NULL; } return ipa; } /* * NAME: ipaddr->del() * DESCRIPTION: delete an ipaddr */ static void ipa_del(ipaddr *ipa) { if (--ipa->ref == 0) { if (ipa->prev != (ipaddr *) NULL || qhead == ipa) { /* remove from queue */ if (ipa->prev != (ipaddr *) NULL) { ipa->prev->next = ipa->next; } else { qhead = ipa->next; } if (ipa->next != (ipaddr *) NULL) { ipa->next->prev = ipa->prev; } else { qtail = ipa->prev; } } /* add to free list */ if (flast != (ipaddr *) NULL) { flast->next = ipa; ipa->prev = flast; } else { ffirst = flast = ipa; ipa->prev = (ipaddr *) NULL; } ipa->next = (ipaddr *) NULL; nfree++; } } /* * NAME: ipaddr->lookup() * DESCRIPTION: lookup another ip name */ static void ipa_lookup() { int i; ipaddr *ipa; lookup = FALSE; if (lastreq != (ipaddr *) NULL) { /* read ip name */ if (host.rtnCode == noErr) { i = strlen(host.cname) - 1; if (host.cname[i] == '.') { host.cname[i] = '\0'; } strcpy(lastreq->name, host.cname); } else { lastreq->name[0] = '\0'; } qhead = lastreq->next; if (qhead == (ipaddr *) NULL) { qtail = (ipaddr *) NULL; } } /* if request queue not empty, write new query */ if (qhead != (ipaddr *) NULL) { ipa = qhead; host.cname[0] = '\0'; lookup = busy = TRUE; if (AddrToName(ipa->ipnum, &host, ipa_report, &busy) != cacheFault) { busy = FALSE; } qhead = ipa->next; if (qhead == (ipaddr *) NULL) { qtail = (ipaddr *) NULL; } ipa->prev = ipa->next = (ipaddr *) NULL; lastreq = ipa; } else { lastreq = (ipaddr *) NULL; busy = FALSE; } } # define TCPBUFSZ 8192 struct _connection_ { connection *next; /* next in list */ short qType; /* queue type */ connection *prev; /* prev in list */ connection *hash; /* next in hashed list */ char dflag; /* data arrival flag */ char cflags; /* closing/closed flags */ char sflags; /* status flags */ ipaddr *addr; /* internet address of connection */ unsigned short port; /* port of connection */ int ssize; /* send size */ int bufsz; /* UDP buffer size */ char *udpbuf; /* UDP read buffer */ TCPiopb iobuf; /* I/O parameter buffer */ struct wdsEntry wds[2]; /* WDS */ }; # define TCP_DATA 0x01 /* data available */ # define TCP_CLOSING 0x01 /* shutdown on other side */ # define TCP_TERMINATED 0x02 /* terminated */ # define TCP_OPEN 0x01 /* open */ # define TCP_CLOSE 0x02 /* closing connection */ # define TCP_BLOCKED 0x04 /* input blocked */ # define TCP_SEND 0x08 /* writing data */ # define TCP_WAIT 0x10 /* waiting for data to be written */ # define TCP_RELEASED 0x20 /* (about to be) released */ static connection *connlist; /* list of open connections */ static QHdr flist; /* free connection queue */ static QHdr telnet; /* telnet accept queue */ static QHdr binary; /* binary accept queue */ static int tcpbufsz; /* TCP buffer size */ static connection **udphtab; /* UDP hash table */ static int udphtabsz; /* UDP hash table size */ static unsigned int telnet_port; /* telnet port number */ static unsigned int binary_port; /* binary port number */ static UDPiopb udpbuf; /* UDP I/O buffer */ static bool udpdata; /* UDP data ready */ /* * NAME: asr() * DESCRIPTION: asynchronous notification */ static pascal void asr(StreamPtr stream, unsigned short event, Ptr userdata, unsigned short term, struct ICMPReport *icmp) { connection *conn; conn = (connection *) userdata; switch (event) { case TCPDataArrival: case TCPUrgent: conn->dflag = TCP_DATA; break; case TCPClosing: conn->cflags |= TCP_CLOSING; break; case TCPTerminate: conn->cflags |= TCP_TERMINATED; break; /* * currently ignored: * ULP timeout */ } } /* * NAME: conn->start() * DESCRIPTION: start listening on a TCP stream */ static void conn_start(connection *conn, unsigned int port) { conn->dflag = 0; conn->cflags = 0; conn->sflags = 0; /* * start listening */ conn->iobuf.ioResult = inProgress; conn->iobuf.csCode = TCPPassiveOpen; conn->iobuf.csParam.open.ulpTimeoutValue = 255; conn->iobuf.csParam.open.ulpTimeoutAction = 0; /* report & repeat */ conn->iobuf.csParam.open.validityFlags = timeoutValue | timeoutAction; conn->iobuf.csParam.open.commandTimeoutValue = 0; conn->iobuf.csParam.open.remoteHost = 0; conn->iobuf.csParam.open.remotePort = 0; conn->iobuf.csParam.open.localHost = 0; conn->iobuf.csParam.open.localPort = port; conn->iobuf.csParam.open.dontFrag = 0; conn->iobuf.csParam.open.timeToLive = 0; conn->iobuf.csParam.open.security = 0; conn->iobuf.csParam.open.optionCnt = 0; conn->iobuf.csParam.open.userDataPtr = (Ptr) conn; PBControlAsync((ParmBlkPtr) &conn->iobuf); } /* * NAME: tcpcompletion() * DESCRIPTION: conn start completion */ static void tcpcompletion(struct TCPiopb *iobuf) { connection *conn; QHdr *queue; conn = (connection *) iobuf->csParam.open.userDataPtr; if (conn->sflags & TCP_RELEASED) { return; } if (iobuf->ioResult == noErr) { queue = (iobuf->csParam.open.localPort == telnet_port) ? &telnet : &binary; conn->sflags = TCP_OPEN; /* opened */ if (flist.qHead == NULL) { /* cannot start listening on another one right away, alas */ queue->qFlags = FALSE; return; } conn = (connection *) flist.qHead; Dequeue((QElemPtr) conn, &flist); Enqueue((QElemPtr) conn, queue); } /* (re)start */ conn_start(conn, iobuf->csParam.open.localPort); } /* * NAME: udpcompletion() * DESCRIPTION: udp message received */ static void udpcompletion(struct UDPiopb *iobuf) { udpdata = TRUE; } /* * NAME: conn->init() * DESCRIPTION: initialize connections */ bool conn_init(int nusers, unsigned int t_port, unsigned int b_port) { IOParam device; GetAddrParamBlock addr; connection *conn; connlist = NULL; memset(&flist, '\0', sizeof(flist)); memset(&telnet, '\0', sizeof(telnet)); memset(&binary, '\0', sizeof(binary)); /* initialize MacTCP */ device.ioNamePtr = "\p.IPP"; device.ioVRefNum = 0; device.ioVersNum = 0; device.ioPermssn = fsCurPerm; device.ioMisc = 0; if (PBOpenSync((ParmBlkPtr) &device) != noErr) { P_message("Config error: cannot initialize MacTCP\012"); /* LF */ return FALSE; } if (!ipa_init(nusers)) { P_message("Config error: cannot initialize DNR\012"); /* LF */ return FALSE; } addr.ioCRefNum = device.ioRefNum; addr.csCode = ipctlGetAddr; if (PBControlSync((ParmBlkPtr) &addr) != noErr) { P_message("Config error: cannot get host address\012"); /* LF */ return FALSE; } udpbuf.ioCRefNum = device.ioRefNum; udpbuf.csCode = UDPMaxMTUSize; udpbuf.csParam.mtu.remoteHost = addr.ourAddress; if (PBControlSync((ParmBlkPtr) &udpbuf) != noErr) { P_message("Config error: cannot get MTU size\012"); /* LF */ return FALSE; } tcpbufsz = 4 * udpbuf.csParam.mtu.mtuSize + 1024; if (tcpbufsz < TCPBUFSZ) { tcpbufsz = TCPBUFSZ; } udpbuf.csCode = UDPCreate; udpbuf.csParam.create.rcvBuff = (Ptr) ALLOC(char, 32768); udpbuf.csParam.create.rcvBuffLen = 32768; udpbuf.csParam.create.notifyProc = NULL; udpbuf.csParam.create.localPort = b_port; if (PBControlSync((ParmBlkPtr) &udpbuf) != noErr) { P_message("Config error: cannot open UDP port\012"); /* LF */ return FALSE; } udphtab = ALLOC(connection*, udphtabsz = nusers); memset(udphtab, '\0', udphtabsz * sizeof(connection*)); /* initialize TCP streams */ if (nusers < 2) { nusers = 2; } conn = ALLOC(connection, nusers); while (--nusers >= 0) { /* open TCP stream */ conn->iobuf.ioCRefNum = device.ioRefNum; conn->iobuf.csCode = TCPCreate; conn->iobuf.csParam.create.rcvBuff = (Ptr) ALLOC(char, tcpbufsz); conn->iobuf.csParam.create.rcvBuffLen = tcpbufsz; conn->iobuf.csParam.create.notifyProc = asr; conn->iobuf.csParam.create.userDataPtr = (Ptr) conn; if (PBControlSync((ParmBlkPtr) &conn->iobuf) != noErr) { /* failed (too many TCP streams?) */ FREE(conn->iobuf.csParam.create.rcvBuff); break; } conn->iobuf.ioCompletion = tcpcompletion; conn->qType = 0; Enqueue((QElemPtr) conn, &flist); conn++; } telnet_port = t_port; binary_port = b_port; return TRUE; } /* * NAME: conn->release() * DESCRIPTION: (forcibly) release all TCP streams in a list of connections */ static void conn_release(connection *conn) { TCPiopb iobuf; while (conn != NULL) { conn->sflags |= TCP_RELEASED; iobuf = conn->iobuf; iobuf.csCode = TCPRelease; PBControlSync((ParmBlkPtr) &iobuf); conn = conn->next; } } /* * NAME: conn->finish() * DESCRIPTION: terminate connections */ void conn_finish(void) { UDPiopb iobuf; /* remove all existing connections */ conn_release(connlist); conn_release((connection *) telnet.qHead); conn_release((connection *) binary.qHead); conn_release((connection *) flist.qHead); /* close UDP port */ iobuf = udpbuf; iobuf.csCode = UDPRelease; PBControlSync((ParmBlkPtr) &iobuf); ipa_finish(); } /* * NAME: conn->listen() * DESCRIPTION: start listening on telnet port and binary port */ void conn_listen(void) { connection *tconn, *bconn; tconn = (connection *) flist.qHead; Dequeue((QElemPtr) tconn, &flist); bconn = (connection *) flist.qHead; Dequeue((QElemPtr) bconn, &flist); /* start listening on telnet port */ Enqueue((QElemPtr) tconn, &telnet); telnet.qFlags = TRUE; conn_start(tconn, telnet_port); /* start listening on binary port */ Enqueue((QElemPtr) bconn, &binary); binary.qFlags = TRUE; conn_start(bconn, binary_port); /* start reading on UDP port */ udpdata = FALSE; udpbuf.ioCompletion = udpcompletion; udpbuf.csCode = UDPRead; udpbuf.csParam.receive.timeOut = 0; udpbuf.csParam.receive.secondTimeStamp = 0; PBControlAsync((ParmBlkPtr) &udpbuf); } /* * NAME: conn->accept() * DESCRIPTION: accept a new connection on a port */ static connection *conn_accept(QHdr *queue, unsigned int portno) { connection *conn; conn = (connection *) queue->qHead; if (conn != NULL && conn->sflags == TCP_OPEN) { Dequeue((QElemPtr) conn, queue); conn->prev = NULL; conn->next = connlist; if (connlist != NULL) { connlist->prev = conn; } connlist = conn; /* fully initialize connection struct */ conn->iobuf.ioCompletion = NULL; conn->addr = ipa_new(conn->iobuf.csParam.open.remoteHost); conn->port = conn->iobuf.csParam.open.remotePort; conn->ssize = 0; conn->udpbuf = (char *) NULL; conn->wds[0].length = 0; m_static(); conn->wds[0].ptr = (Ptr) ALLOC(char, tcpbufsz); m_dynamic(); conn->wds[1].length = 0; conn->wds[1].ptr = NULL; return conn; } else { return NULL; } } /* * NAME: conn->tnew() * DESCRIPTION: accept a new telnet connection */ connection *conn_tnew(void) { return conn_accept(&telnet, telnet_port); } /* * NAME: conn->bnew() * DESCRIPTION: accept a new binary connection */ connection *conn_bnew(void) { return conn_accept(&binary, binary_port); } /* * NAME: conn->udp() * DESCRIPTION: enable the UDP channel of a binary connection */ void conn_udp(connection *conn) { connection **hash; m_static(); conn->udpbuf = ALLOC(char, BINBUF_SIZE); m_dynamic(); conn->bufsz = -1; hash = &udphtab[(conn->addr->ipnum ^ conn->port) % udphtabsz]; conn->hash = *hash; *hash = conn; } /* * NAME: conn->flush() * DESCRIPTION: flush data on a connection, return TRUE if done */ static bool conn_flush(connection *conn) { if (conn->sflags & TCP_SEND) { if (conn->iobuf.ioResult == inProgress) { return FALSE; /* send still in progress */ } if (conn->iobuf.ioResult != noErr) { /* send failed */ return TRUE; /* can't send any more */ } conn->sflags &= ~TCP_SEND; if (conn->ssize != 0) { /* copy overlapping block */ memcpy(conn->wds[0].ptr, (char *) conn->wds[0].ptr + conn->wds[0].length, conn->ssize); } } if (conn->ssize != 0) { /* more to send */ conn->iobuf.ioResult = inProgress; conn->iobuf.csCode = TCPSend; conn->iobuf.csParam.send.ulpTimeoutValue = 120; conn->iobuf.csParam.send.ulpTimeoutAction = 1; /* abort */ conn->iobuf.csParam.send.validityFlags = timeoutValue | timeoutAction; conn->iobuf.csParam.send.pushFlag = 1; /* send right away */ conn->iobuf.csParam.send.urgentFlag = 0; conn->wds[0].length = conn->ssize; conn->ssize = 0; conn->iobuf.csParam.send.wdsPtr = (Ptr) conn->wds; PBControlAsync((ParmBlkPtr) &conn->iobuf); if (conn->iobuf.ioResult == inProgress) { conn->sflags |= TCP_SEND; return FALSE; } } conn->wds[0].length = 0; return TRUE; } /* * NAME: conn->del() * DESCRIPTION: delete a connection */ void conn_del(connection *conn) { connection **hash; conn->sflags |= TCP_CLOSE; if (!(conn->cflags & TCP_TERMINATED)) { if (!conn_flush(conn)) { /* buffer not flushed */ return; } if (conn->sflags & TCP_OPEN) { /* close connection */ conn->sflags &= ~TCP_OPEN; conn->iobuf.ioResult = inProgress; conn->iobuf.csCode = TCPClose; conn->iobuf.csParam.close.ulpTimeoutValue = 60; conn->iobuf.csParam.close.ulpTimeoutAction = 1; /* abort */ conn->iobuf.csParam.close.validityFlags = timeoutValue | timeoutAction; PBControlAsync((ParmBlkPtr) &conn->iobuf); } if (conn->iobuf.ioResult == inProgress) { return; } conn->iobuf.csCode = TCPAbort; PBControlSync((ParmBlkPtr) &conn->iobuf); } /* prepare for new use */ FREE(conn->wds[0].ptr); conn->iobuf.ioCompletion = tcpcompletion; if (conn->udpbuf != (char *) NULL) { FREE(conn->udpbuf); for (hash = &udphtab[(conn->addr->ipnum ^ conn->port) % udphtabsz]; *hash != conn; hash = &(*hash)->hash) ; *hash = conn->hash; } ipa_del(conn->addr); /* * put in free list */ if (conn->prev != NULL) { conn->prev->next = conn->next; } else { connlist = conn->next; } if (conn->next != NULL) { conn->next->prev = conn->prev; } Enqueue((QElemPtr) conn, &flist); /* * see if connection can be re-used right away */ if (!telnet.qFlags) { if (Dequeue((QElemPtr) conn, &flist) == noErr) { telnet.qFlags = TRUE; Enqueue((QElemPtr) conn, &telnet); conn_start(conn, telnet_port); } } else if (!binary.qFlags) { if (Dequeue((QElemPtr) conn, &flist) == noErr) { binary.qFlags = TRUE; Enqueue((QElemPtr) conn, &binary); conn_start(conn, binary_port); } } } /* * NAME: conn->block() * DESCRIPTION: block or unblock input from a connection */ void conn_block(connection *conn, int flag) { if (flag) { conn->sflags |= TCP_BLOCKED; } else { conn->sflags &= ~TCP_BLOCKED; } } /* * NAME: conn->select() * DESCRIPTION: wait for input from connections */ int conn_select(int wait) { long ticks; bool stop; connection *conn, *next, **hash; ticks = TickCount() + wait * 60; stop = FALSE; do { getevent(); if (lookup && !busy) { ipa_lookup(); } if (udpdata) { hash = &udphtab[(udpbuf.csParam.receive.remoteHost ^ udpbuf.csParam.receive.remotePort) % udphtabsz]; while (*hash != (connection *) NULL) { if ((*hash)->addr->ipnum == udpbuf.csParam.receive.remoteHost && (*hash)->port == udpbuf.csParam.receive.remotePort) { /* * copy to connection's buffer */ memcpy((*hash)->udpbuf, udpbuf.csParam.receive.rcvBuff, (*hash)->bufsz = udpbuf.csParam.receive.rcvBuffLen); break; } hash = &(*hash)->hash; } /* else from unknown source: ignore */ udpdata = FALSE; udpbuf.csCode = UDPBfrReturn; PBControlSync((ParmBlkPtr) &udpbuf); udpbuf.ioCompletion = udpcompletion; udpbuf.csCode = UDPRead; PBControlAsync((ParmBlkPtr) &udpbuf); stop = TRUE; } for (conn = connlist; conn != NULL; conn = next) { next = conn->next; if (conn->sflags & TCP_CLOSE) { conn_del(conn); } else { conn_flush(conn); if ((conn->dflag && !(conn->sflags & TCP_BLOCKED)) || conn->cflags || ((conn->sflags & TCP_WAIT) && conn->wds[0].length + conn->ssize != tcpbufsz)) { stop = TRUE; } } } if (stop) { return 1; } conn = (connection *) telnet.qHead; if (conn != NULL && conn->sflags == TCP_OPEN) { return 1; /* new telnet connection */ } conn = (connection *) binary.qHead; if (conn != NULL && conn->sflags == TCP_OPEN) { return 1; /* new binary connection */ } } while (TickCount() - ticks < 0); return 0; } /* * NAME: conn->read() * DESCRIPTION: read from a connection */ int conn_read(connection *conn, char *buf, unsigned int len) { TCPiopb iobuf; if (conn->cflags) { return -1; /* terminated */ } if (!(conn->dflag) || (conn->sflags & TCP_BLOCKED)) { return 0; /* no data */ } conn->dflag = 0; /* get amount of available data */ iobuf = conn->iobuf; iobuf.csCode = TCPStatus; if (PBControlSync((ParmBlkPtr) &iobuf) != noErr) { return -1; /* can't get status */ } if (len > iobuf.csParam.status.amtUnreadData) { len = iobuf.csParam.status.amtUnreadData; } /* get available data */ iobuf.csCode = TCPRcv; iobuf.csParam.receive.commandTimeoutValue = 0; iobuf.csParam.receive.rcvBuff = (Ptr) buf; iobuf.csParam.receive.rcvBuffLen = len; if (PBControlSync((ParmBlkPtr) &iobuf) != noErr || len != iobuf.csParam.receive.rcvBuffLen) { return -1; } return len; } /* * NAME: conn->udpread() * DESCRIPTION: read a message from a UDP channel */ int conn_udpread(connection *conn, char *buf, unsigned int len) { if (conn->bufsz >= 0) { /* udp buffer is not empty */ if (conn->bufsz <= len) { memcpy(buf, conn->udpbuf, len = conn->bufsz); } else { len = 0; } conn->bufsz = -1; return len; } return -1; } /* * NAME: conn->write() * DESCRIPTION: write to a connection; return the amount of bytes written */ int conn_write(connection *conn, char *buf, unsigned int len) { int size; if (conn->cflags) { return 0; } conn->sflags &= ~TCP_WAIT; if (len == 0) { return 0; /* send_message("") can be used to flush buffer */ } size = tcpbufsz - (conn->wds[0].length + conn->ssize); if (size == 0) { conn->sflags |= TCP_WAIT; return -1; } if (len > size) { len = size; conn->sflags |= TCP_WAIT; } memcpy((char *) conn->wds[0].ptr + conn->wds[0].length + conn->ssize, buf, len); conn->ssize += len; conn_flush(conn); return len; } /* * NAME: conn->udpwrite() * DESCRIPTION: write a message to a UDP channel */ int conn_udpwrite(connection *conn, char *buf, unsigned int len) { struct wdsEntry wds[2]; UDPiopb iobuf; if (conn->cflags) { return 0; } wds[0].ptr = (Ptr) buf; wds[0].length = len; wds[1].ptr = NULL; wds[1].length = 0; iobuf = udpbuf; iobuf.csCode = UDPWrite; iobuf.csParam.send.remoteHost = conn->addr->ipnum; iobuf.csParam.send.remotePort = conn->port; iobuf.csParam.send.wdsPtr = (Ptr) wds; iobuf.csParam.send.checkSum = 1; iobuf.csParam.send.sendLength = 0; return (PBControlSync((ParmBlkPtr) &iobuf) != noErr) ? -1 : len; } /* * NAME: conn->wrdone() * DESCRIPTION: return TRUE if a connection is ready for output */ bool conn_wrdone(connection *conn) { if (conn->cflags || !(conn->sflags & TCP_WAIT)) { return TRUE; } if (conn->wds[0].length + conn->ssize != tcpbufsz) { conn->sflags &= ~TCP_WAIT; return TRUE; } return FALSE; } /* * NAME: conn->ipnum() * DESCRIPTION: return the ip number of a connection */ char *conn_ipnum(connection *conn) { static char buf[16]; unsigned long ipnum; ipnum = conn->addr->ipnum; sprintf(buf, "%d.%d.%d.%d", UCHAR(ipnum >> 24), UCHAR(ipnum >> 16), UCHAR(ipnum >> 8), UCHAR(ipnum)); return buf; } /* * NAME: conn->ipname() * DESCRIPTION: return the ip name of a connection */ char *conn_ipname(connection *conn) { return (conn->addr->name[0] != '\0') ? conn->addr->name : conn_ipnum(conn); }