]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - opentpt.c
Various minor tweaks
[PuTTY.git] / opentpt.c
1 /* $Id: opentpt.c,v 1.1.2.3 1999/09/01 22:24:41 ben Exp $ */
2 /*
3  * Copyright (c) 1999 Ben Harris
4  * All rights reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation
8  * files (the "Software"), to deal in the Software without
9  * restriction, including without limitation the rights to use,
10  * copy, modify, merge, publish, distribute, sublicense, and/or
11  * sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following
13  * conditions:
14  * 
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  * 
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27
28 #include <MacTypes.h>
29 #include <CodeFragments.h>
30 #include <OpenTransport.h>
31 #include <OpenTptInternet.h>
32
33 #include <stdio.h>
34 #include <string.h>
35
36 #include "putty.h"
37
38 /* See the top of macnet.c for some idea of how this is meant to work. */
39
40 struct otpt_socket {
41     EndpointRef ep;
42     Session *sess;
43     OTLIFO sendq;
44     OTLIFO eventq;
45     long eventhandler;
46 };
47
48 struct otpt_netevent {
49     OTLink next;
50     Net_Event_Type type;
51 };
52
53 static int otpt_init(void);
54 static void otpt_shutdown(void);
55 static void otpt_poll(void);
56 static void *otpt_open(Session *, char const *, int);
57 static int otpt_recv(void *, void *, int, int);
58 static int otpt_send(void *, void *, int, int);
59 static void otpt_close(void *);
60 static void otpt_destroy(void *);
61 static pascal void otpt_notifier(void *, OTEventCode, OTResult , void *);
62 static void otpt_sendevent(struct otpt_socket *, Net_Event_Type);
63 static pascal void otpt_rcvevent(void *);
64
65 Network_Stack otpt_stack = {
66     otpt_init, otpt_open, otpt_recv, otpt_send, otpt_poll, otpt_close,
67     otpt_destroy, otpt_shutdown
68 };
69
70 static OTConfiguration *otpt_config = kOTInvalidConfigurationPtr;
71
72 static int otpt_init(void) {
73     OSErr err;
74
75 #if TARGET_RT_MAC_CFM
76     /* Check that the OpenTransport libraries were there (really just ppc) */
77     if (&InitOpenTransport == kUnresolvedCFragSymbolAddress)
78         return 1;
79 #endif
80     err = InitOpenTransport();
81     if (err != noErr)
82         return err;
83     otpt_config = OTCreateConfiguration("tcp");
84     if (otpt_config == kOTInvalidConfigurationPtr ||
85         otpt_config == kOTNoMemoryConfigurationPtr)
86         return 1;
87     return 0;
88 }
89
90 /* Stuff below here is only needed if you actually have Open Transport. */
91 /* #pragma segment OpenTpt */
92 /* Last I looked, this only produced a 1.5k segment, which isn't worth it. */
93
94 /*
95  * This should only be called once all the connections have been
96  * closed (we don't bother keeping a table of them).
97  */
98
99 void otpt_shutdown(void) {
100
101     CloseOpenTransport();
102 }
103
104 static void *otpt_open(Session *sess, char const *host, int port) {
105     struct otpt_socket *s = NULL;
106     OSStatus err;
107     TCall remote;
108     DNSAddress *remoteaddr;
109     
110     s = smalloc(sizeof(*s));
111     memset(s, 0, sizeof(*s));
112
113     /* Get a TCP endpoint (equiv of socket()) */
114     s->ep = OTOpenEndpoint(OTCloneConfiguration(otpt_config), 0, NULL, &err);
115     if (err != kOTNoError || s->ep == NULL) goto splat;
116
117     /* Set up a system-task-time event handler (scheduled by the notifier) */
118     s->eventhandler = OTCreateSystemTask(&otpt_rcvevent, (void *)s);
119     if (s->eventhandler == 0) goto splat;
120     /* Attach our notifier function (note that this is _not_ a UPP) */
121     err = OTInstallNotifier(s->ep, otpt_notifier, (void *)s);
122     if (err != kOTNoError) goto splat;
123
124     /* Bind to any local address */
125     err = OTBind(s->ep, NULL, NULL);
126     if (err != kOTNoError) goto splat;
127     memset(&remote, 0, sizeof(remote));
128     remoteaddr = smalloc(sizeof(*remoteaddr) - sizeof(remoteaddr->fName) +
129                          strlen(host) + 7); /* allow space for port no. */
130     remote.addr.buf = (UInt8 *)remoteaddr;
131     /* XXX: I don't _think_ OTInitDNSAddress can modify the hostname. */
132     remote.addr.len = OTInitDNSAddress(remoteaddr, (char *)host);
133     remote.addr.len += sprintf(&remoteaddr->fName[strlen(remoteaddr->fName)],
134                                ":%d", port);
135     /* Asynchronous blocking mode, so we don't have to wait */
136     err = OTSetAsynchronous(s->ep);
137     if (err != kOTNoError) goto splat;
138     err = OTSetBlocking(s->ep);
139     if (err != kOTNoError) goto splat;
140     err = OTConnect(s->ep, &remote, NULL);
141     if (err != kOTNoDataErr)
142         goto splat;
143     return s;
144
145   splat:
146     otpt_destroy(s);
147     return NULL;
148 }
149
150 static int otpt_recv(void *sock, void *buf, int buflen, int flags) {
151     struct otpt_socket *s = (struct otpt_socket *)sock;
152     OTResult result;
153     OTFlags otflags;
154
155     OTSetNonBlocking(s->ep);
156     OTSetSynchronous(s->ep);
157     result = OTRcv(s->ep, buf, buflen, &otflags);
158     if (result >= 0)
159         return result;
160     else if (result == kOTNoDataErr)
161         return 0;
162     else /* confusion! */
163         return 0;
164 }
165
166 static void otpt_poll(void) {
167
168 }
169
170 static int otpt_send(void *sock, void *buf, int buflen, int flags) {
171     struct otpt_socket *s = (struct otpt_socket *)sock;
172
173     /* XXX: using blocking mode is bogus, but it's far easier than not. */
174     OTSetSynchronous(s->ep);
175     OTSetBlocking(s->ep);
176     return OTSnd(s->ep, buf, buflen, flags);
177 }
178
179 /*
180  * Politely ask the other end to close the connection.
181  */
182
183 static void otpt_close(void *sock) {
184     struct otpt_socket *s = (struct otpt_socket *)sock;
185
186     /* XXX: using blocking mode is bogus, but it's far easier than not. */
187     OTSetSynchronous(s->ep);
188     OTSetBlocking(s->ep);
189     OTSndOrderlyDisconnect(s->ep);
190 }
191
192 /*
193  * This should take a socket in any state and undo it all, freeing any
194  * allocated memory and generally making it safe to forget about it.
195  * It should only be called at system task time.
196  */
197
198 static void otpt_destroy(void *sock) {
199     struct otpt_socket *s = (struct otpt_socket *)sock;
200     OSStatus err;
201     OTLink *link;
202
203     if (s == NULL)
204         return;
205
206     /* Tear down the connection */
207     /* If we ever start using T_MEMORYRELEASED, we need to be careful here. */
208     err = OTSetSynchronous(s->ep);
209     if (err == kOTNoError)
210         err = OTSetNonBlocking(s->ep);
211     if (err == kOTNoError)
212         err = OTCloseProvider(s->ep);
213     
214     /* Stop the event handler running */
215     if (s->eventhandler != 0)
216         OTDestroySystemTask(s->eventhandler);
217
218     /* Flush the event and send queues */
219     while ((link = OTLIFODequeue(&s->eventq)) != NULL)
220         OTFreeMem(link);
221     while ((link = OTLIFODequeue(&s->sendq)) != NULL)
222         OTFreeMem(link);
223
224     /* Finally, free the socket record itself */
225     sfree(s);
226 }
227
228 /*
229  * Any asynchronous events OpenTransport wants to tell us about end up
230  * here.  This function may be called at deferred task or system task
231  * time, and must be re-entrant.
232  */
233
234 static pascal void otpt_notifier(void *contextPtr, OTEventCode code,
235                                     OTResult result, void *cookie) {
236     struct otpt_socket *s = (struct otpt_socket *)contextPtr;
237     OSStatus status;
238     TDiscon discon;
239
240     switch (code) {
241       case T_CONNECT: /* OTConnect completed */
242         status = OTRcvConnect(s->ep, NULL); /* XXX do we want the new TCall? */
243         if (status == kOTNoDataErr)
244             break;
245         else if (status != kOTNoError) {
246             otpt_sendevent(s, NE_DIED);
247             break;
248         }
249         /* Synchronous non-blocking mode for normal data transfer */
250         OTSetSynchronous(s->ep);
251         OTSetNonBlocking(s->ep);
252         otpt_sendevent(s, NE_OPEN);
253         break;
254       case T_DATA:
255         otpt_sendevent(s, NE_DATA);
256         break;
257       case T_EXDATA:
258         otpt_sendevent(s, NE_URGENT);
259         break;
260       case T_DISCONNECT: /* Disconnection complete or OTConnect rejected */
261         memset(&discon, 0, sizeof(discon));
262         /*
263          * This function returns a positive error code. To obtain the
264          * negative error code, subtract that positive value from
265          * -3199.
266          */
267         status = OTRcvDisconnect(s->ep, &discon);
268         if (cookie == NULL) /* spontaneous disconnect */
269             switch (E2OSStatus(discon.reason)) {
270               case kECONNRESETErr:
271                 otpt_sendevent(s, NE_ABORT);
272                 break;
273               case kETIMEDOUTErr:
274                 otpt_sendevent(s, NE_TIMEOUT);
275                 break;
276               default:
277                 otpt_sendevent(s, NE_DIED);
278                 break;
279             }
280         else /* failed connect */
281             otpt_sendevent(s, NE_NOOPEN);
282       case T_ORDREL:
283         OTRcvOrderlyDisconnect(s->ep);
284         otpt_sendevent(s, NE_CLOSING);
285     }
286 }
287
288 /*
289  * This function is called at interrupt time (or thereabouts) to
290  * dispatch an event that has to be handled at system task time.
291  * Network backends will expect their msg entries to be called then.
292  */
293
294 static void otpt_sendevent(struct otpt_socket *s, Net_Event_Type type) {
295     struct otpt_netevent *ne;
296
297     ne = OTAllocMem(sizeof(*ne));
298     if (ne == NULL)
299         fatalbox("OTAllocMem failed.  Aargh!");
300     ne->type = type;
301     OTLIFOEnqueue(&s->eventq, &ne->next);
302     /* Schedule something */
303     OTScheduleSystemTask(s->eventhandler);
304 }
305
306 /*
307  * Pull one or more network events off a socket's queue and handle
308  * them.  Keep going until we run out (events may be getting enqueued
309  * while we're running).  This is mildly evil as it'll prevent any
310  * other task running if we're under heavy load.
311  */
312
313 static pascal void otpt_rcvevent(void *arg) {
314     struct otpt_socket *s = (struct otpt_socket *)arg;
315     OTLink *link;
316     struct otpt_netevent *ne;
317
318     while ((link = OTLIFOStealList(&s->eventq)) != NULL) {
319         link = OTReverseList(link);
320         while (link != NULL) {
321             ne = (struct otpt_netevent *)link;
322             link = ne->next.fNext;
323             switch (ne->type) {
324               default:
325                 (s->sess->back->msg)(s->sess, s, ne->type);
326                 break;
327             }
328             OTFreeMem(ne);
329         }
330     }
331 }    
332
333 /*
334  * Local Variables:
335  * c-file-style: "simon"
336  * End:
337  */ 
338
339