mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
b73241d17c
[originally from svn r218]
344 lines
9.8 KiB
C
344 lines
9.8 KiB
C
/* $Id: opentpt.c,v 1.1.2.4 1999/09/07 23:41: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 <MacTypes.h>
|
|
#include <CodeFragments.h>
|
|
#include <OpenTransport.h>
|
|
#include <OpenTptInternet.h>
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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_init(void);
|
|
static void otpt_shutdown(void);
|
|
static void otpt_poll(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;
|
|
|
|
#if TARGET_RT_MAC_CFM
|
|
/* Check that the OpenTransport libraries were there (really just ppc) */
|
|
if (&InitOpenTransport == kUnresolvedCFragSymbolAddress)
|
|
return 1;
|
|
#endif
|
|
err = InitOpenTransport();
|
|
if (err != noErr)
|
|
return err;
|
|
otpt_config = OTCreateConfiguration("tcp");
|
|
if (otpt_config == kOTInvalidConfigurationPtr ||
|
|
otpt_config == kOTNoMemoryConfigurationPtr)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* Stuff below here is only needed if you actually have Open Transport. */
|
|
/* #pragma segment OpenTpt */
|
|
/* Last I looked, this only produced a 1.5k segment, which isn't worth it. */
|
|
|
|
/*
|
|
* 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;
|
|
|
|
assert(otpt_config != kOTInvalidConfigurationPtr);
|
|
s = smalloc(sizeof(*s));
|
|
memset(s, 0, sizeof(*s));
|
|
s->sess = sess;
|
|
|
|
/* Get a TCP endpoint (equiv of socket()) */
|
|
s->ep = OTOpenEndpoint(OTCloneConfiguration(otpt_config), 0, NULL, &err);
|
|
if (err != kOTNoError || s->ep == NULL) goto splat;
|
|
|
|
/* Set up a system-task-time event handler (scheduled by the notifier) */
|
|
s->eventhandler = OTCreateSystemTask(&otpt_rcvevent, (void *)s);
|
|
if (s->eventhandler == 0) 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;
|
|
|
|
/* 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;
|
|
/* XXX: I don't _think_ OTInitDNSAddress can modify the hostname. */
|
|
remote.addr.len = OTInitDNSAddress(remoteaddr, (char *)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 otflags;
|
|
|
|
OTSetNonBlocking(s->ep);
|
|
OTSetSynchronous(s->ep);
|
|
result = OTRcv(s->ep, buf, buflen, &otflags);
|
|
if (result >= 0)
|
|
return result;
|
|
else if (result == kOTNoDataErr)
|
|
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 only 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);
|
|
Debugger();
|
|
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 going 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;
|
|
|
|
while ((link = OTLIFOStealList(&s->eventq)) != NULL) {
|
|
link = OTReverseList(link);
|
|
while (link != NULL) {
|
|
ne = (struct otpt_netevent *)link;
|
|
link = ne->next.fNext;
|
|
switch (ne->type) {
|
|
default:
|
|
(s->sess->back->msg)(s->sess, s, ne->type);
|
|
break;
|
|
}
|
|
OTFreeMem(ne);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Local Variables:
|
|
* c-file-style: "simon"
|
|
* End:
|
|
*/
|
|
|
|
|