/* $Id: opentpt.c,v 1.1.2.1 1999/08/02 08:06:32 ben Exp $ */ /* * Copyright (c) 1999 Ben Harris * All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #incldue #include #include "putty.h" /* See the top of macnet.c for some idea of how this is meant to work. */ struct otpt_socket { EndpointRef ep; Session *sess; OTLIFO *sendq; OTLIFO *eventq; long eventhandler; }; struct otpt_netevent { OTLink *next; Net_Event_Type type; }; static int otpt_probe(void); static void otpt_init(void); static void *otpt_open(Session *, char const *, int); static int otpt_recv(void *, void *, int, int); static int otpt_send(void *, void *, int, int); static void otpt_close(void *); static void otpt_destroy(void *); static pascal void otpt_notifier(void *, OTEventCode, OTResult , void *); static void otpt_sendevent(struct otpt_socket *, Net_Event_Type); static pascal void otpt_rcvevent(void *); Network_Stack otpt_stack = { otpt_init, otpt_open, otpt_recv, otpt_send, otpt_poll, otpt_close, otpt_destroy, otpt_shutdown }; static OTConfiguration *otpt_config = kOTInvalidCOnfigurationPtr; static int otpt_init(void) { OSErr err; err = InitOpenTransport(); if (err != noErr) return err; otpt_config = OTCreateConfiguration("tcp"); if (otpt_config == kOTInvalidConfigurationPtr || otpt_config == kOTNoMemoryConfigurationPtr) return 1; return 0; } /* * This should only be called once all the connections have been * closed (we don't bother keeping a table of them). */ void otpt_shutdown(void) { CloseOpenTransport(); } static void *otpt_open(Session *sess, char const *host, int port) { struct otpt_socket *s = NULL; OSStatus err; TCall remote; DNSAddress *remoteaddr; s = smalloc(sizeof(*s)); memset(s, 0, sizeof(*s)); /* Get a TCP endpoint (equiv of socket()) */ s->ep = OTOpenEndpoint(OTCloneConfiguration(otpt_config), 0, NULL, &err); if (err != kOTNoError || s->ep == NULL) goto splat; /* Attach our notifier function (note that this is _not_ a UPP) */ err = OTInstallNotifier(s->ep, otpt_notifier, (void *)s); if (err != kOTNoError) goto splat; s->eventhandler = OTCreateSystemTask(&otpt_rcvevent, (void *)s); if (s->eventhandler = 0) goto splat; /* Bind to any local address */ err = OTBind(s->ep, NULL, NULL); if (err != kOTNoError) goto splat; memset(&remote, 0, sizeof(remote)); remoteaddr = smalloc(sizeof(*remoteaddr) - sizeof(remoteaddr->fName) + strlen(host) + 7); /* allow space for port no. */ remote.addr.buf = (UInt8 *)remoteaddr; remote.addr.len = OTInitDNSAddress(remoteaddr, host); remote.addr.len += sprintf(&remoteaddr->fName[strlen(remoteaddr->fName)], ":%d", port); /* Asynchronous blocking mode, so we don't have to wait */ err = OTSetAsynchronous(s->ep); if (err != kOTNoError) goto splat; err = OTSetBlocking(s->ep); if (err != kOTNoError) goto splat; err = OTConnect(s->ep, &remote, NULL); if (err != kOTNoDataErr) goto splat; return s; splat: otpt_destroy(s); return NULL; } static int otpt_recv(void *sock, void *buf, int buflen, int flags) { struct otpt_socket *s = (struct otpt_socket *)sock; OTResult result; OTFlags flags; OTSetNonBlocking(s->ep); OTSetSynchronous(s->ep); result = OTRcv(s->ep, buf, buflen, flags); if (result >= 0) return result; else if (result == kOTNoDataError) return 0; else /* confusion! */ return 0; } static void otpt_poll(void) { } static int otpt_send(void *sock, void *buf, int buflen, int flags) { struct otpt_socket *s = (struct otpt_socket *)sock; /* XXX: using blocking mode is bogus, but it's far easier than not. */ OTSetSynchronous(s->ep); OTSetBlocking(s->ep); return OTSnd(s->ep, buf, buflen, flags); } /* * Politely ask the other end to close the connection. */ static void otpt_close(void *sock) { struct otpt_socket *s = (struct otpt_socket *)sock; /* XXX: using blocking mode is bogus, but it's far easier than not. */ OTSetSynchronous(s->ep); OTSetBlocking(s->ep); OTSndOrderlyDisconnect(s->ep); } /* * This should take a socket in any state and undo it all, freeing any * allocated memory and generally making it safe to forget about it. * It should onlu be called at system task time. */ static void otpt_destroy(void *sock) { struct otpt_socket *s = (struct otpt_socket *)sock; OSStatus err; OTLink *link; if (s == NULL) return; /* Tear down the connection */ /* If we ever start using T_MEMORYRELEASED, we need to be careful here. */ err = OTSetSynchronous(s->ep); if (err == kOTNoError) err = OTSetNonBlocking(s->ep); if (err == kOTNoError) err = OTCloseProvider(s->ep); /* Stop the event handler running */ if (s->eventhandler != 0) OTDestroySystemTask(s->eventhandler); /* Flush the event and send queues */ while ((link = OTLIFODequeue(s->eventq)) != NULL) OTFreeMem(link); while ((link = OTLIFODequeue(s->sendq)) != NULL) OTFreeMem(link); /* Finally, free the socket record itself */ sfree(s); } /* * Any asynchronous events OpenTransport wants to tell us about end up * here. This function may be called at deferred task or system task * time, and must be re-entrant. */ static pascal void otpt_notifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) { struct otpt_socket *s = (struct otpt_socket *)contextPtr; OSStatus status; TDiscon discon; switch (code) { case T_CONNECT: /* OTConnect completed */ status = OTRcvConnect(s->ep, NULL); /* XXX do we want the new TCall? */ if (status == kOTNoDataErr) break; else if (status != kOTNoError) { otpt_sendevent(s, NE_DIED); break; } /* Synchronous non-blocking mode for normal data transfer */ OTSetSynchronous(s->ep); OTSetNonBlocking(s->ep); otpt_sendevent(s, NE_OPEN); break; case T_DATA: otpt_sendevent(s, NE_DATA); break; case T_EXDATA: otpt_sendevent(s, NE_URGENT); break; case T_DISCONNECT: /* Disconnection complete or OTConnect rejected */ memset(&discon, 0, sizeof(discon)); /* * This function returns a positive error code. To obtain the * negative error code, subtract that positive value from * -3199. */ status = OTRcvDisconnect(s->ep, &discon); if (cookie == NULL) /* spontaneous disconnect */ switch (E2OSStatus(discon.reason)) { case kECONNRESETErr: otpt_sendevent(s, NE_ABORT); break; case kETIMEDOUTErr: otpt_sendevent(s, NE_TIMEOUT); break; default: otpt_sendevent(s, NE_DIED); break; } else /* failed connect */ otpt_sendevent(s, NE_NOOPEN); case T_ORDREL: OTRcvOrderlyDisconnect(s->ep); otpt_sendevent(s, NE_CLOSING); } } /* * This function is called at interrupt time (or thereabouts) to * dispatch an event that has to be handled at system task time. * Network backends will expect their msg entries to be called then. */ static void otpt_sendevent(struct otpt_socket *s, Net_Event_Type type) { struct otpt_netevent *ne; ne = OTAllocMem(sizeof(*ne)); if (ne == NULL) fatalbox("OTAllocMem failed. Aargh!"); ne->type = type; OTLIFOEnqueue(&s->eventq, &ne->next); /* Schedule something */ OTScheduleSystemTask(s->eventhandler); } /* * Pull one or more network events off a socket's queue and handle * them. Keep gong until we run out (events may be getting enqueued * while we're running). This is mildly evil as it'll prevent any * other task running if we're under heavy load. */ static pascal void otpt_rcvevent(void *arg) { struct otpt_socket *s = (struct otpt_socket *)arg; OTLink *link; struct otpt_netevent *ne; /* idiom stolen from "Networking With Open Transport". Blame Apple. */ while ((link = OTLIFOStealList(s->eventq)) != NULL) { link = OTReverseList(link); while (link != NULL) { ne = (struct otpt_netevent *)link; link = ne->next; switch (ne->type) { default: (s->sess->back->msg)(s->sess, s, ne->type); break; } OTFreeMem(ne); } } /* * Local Variables: * c-file-style: "simon" * End: */