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