1 |
edhill |
1.1 |
/* macntcp.c -- macintosh nifty application library async TCP routines |
2 |
|
|
*/ |
3 |
|
|
/* (C) Copyright 1995 by Carnegie Mellon University |
4 |
|
|
* All Rights Reserved. |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, distribute, and sell this software |
7 |
|
|
* and its documentation for any purpose is hereby granted without |
8 |
|
|
* fee, provided that the above copyright notice appear in all copies |
9 |
|
|
* and that both that copyright notice and this permission notice |
10 |
|
|
* appear in supporting documentation, and that the name of Carnegie |
11 |
|
|
* Mellon University not be used in advertising or publicity |
12 |
|
|
* pertaining to distribution of the software without specific, |
13 |
|
|
* written prior permission. Carnegie Mellon University makes no |
14 |
|
|
* representations about the suitability of this software for any |
15 |
|
|
* purpose. It is provided "as is" without express or implied |
16 |
|
|
* warranty. |
17 |
|
|
* |
18 |
|
|
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO |
19 |
|
|
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
20 |
|
|
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE |
21 |
|
|
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
22 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
23 |
|
|
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
24 |
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
25 |
|
|
* SOFTWARE. |
26 |
|
|
*/ |
27 |
|
|
/* (C) Copyright 1994-1995 by Christopher J. Newman |
28 |
|
|
* All Rights Reserved. |
29 |
|
|
* |
30 |
|
|
* Permission to use, copy, modify, distribute, and sell this software and its |
31 |
|
|
* documentation for any purpose is hereby granted without fee, provided that |
32 |
|
|
* the above copyright notice appear in all copies and that both that |
33 |
|
|
* copyright notice and this permission notice appear in supporting |
34 |
|
|
* documentation, and that the name of Christopher J. Newman not be used in |
35 |
|
|
* advertising or publicity pertaining to distribution of the software without |
36 |
|
|
* specific, written prior permission. Christopher J. Newman makes no |
37 |
|
|
* representations about the suitability of this software for any purpose. It |
38 |
|
|
* is provided "as is" without express or implied warranty. |
39 |
|
|
* |
40 |
|
|
* CHRISTOPHER J. NEWMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
41 |
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT |
42 |
|
|
* SHALL CHRISTOPHER J. NEWMAN BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
43 |
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
44 |
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
45 |
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
46 |
|
|
* OF THIS SOFTWARE. |
47 |
|
|
* |
48 |
|
|
* Author: Christopher J. Newman |
49 |
|
|
* Message: This is a nifty program. |
50 |
|
|
*/ |
51 |
|
|
|
52 |
|
|
#include <string.h> |
53 |
|
|
#include <MacTCPCommonTypes.h> |
54 |
|
|
#include <TCPPB.h> |
55 |
|
|
#include <AddressXlation.h> |
56 |
|
|
#include <GetMyIPAddr.h> |
57 |
|
|
#include "macnapp.h" |
58 |
|
|
|
59 |
|
|
#define DEF_STREAM_SIZE 8192 |
60 |
|
|
#define MAX_TCPCON 16 |
61 |
|
|
#define RDS 8 |
62 |
|
|
|
63 |
|
|
/* write buffer for TCP writes |
64 |
|
|
*/ |
65 |
|
|
typedef struct tcpwb { |
66 |
|
|
short rused; /* number of RDS used (-1 == in TCPsend) */ |
67 |
|
|
short wused; /* amount of small buffer used */ |
68 |
|
|
rdsEntry rds[RDS+1]; /* array of RDS pointers */ |
69 |
|
|
char fflag[RDS+1]; /* free flags for RDS pointers (1 = call DisposPtr) */ |
70 |
|
|
char *buf; /* write buffer */ |
71 |
|
|
} tcpwb; |
72 |
|
|
|
73 |
|
|
/* structure describing a TCP connection |
74 |
|
|
*/ |
75 |
|
|
typedef struct tcpinfo { |
76 |
|
|
short state; /* current state */ |
77 |
|
|
short rclose; /* remote host wants to close */ |
78 |
|
|
short lclose; /* local host wants to close */ |
79 |
|
|
int havedata:1; /* data is available to read */ |
80 |
|
|
int urgent:1; /* TCP in urgent mode */ |
81 |
|
|
int push:1; /* next write should be pushed */ |
82 |
|
|
int pushed:1; /* last write was pushed */ |
83 |
|
|
int reading:1; /* reading data */ |
84 |
|
|
int server:1; /* is a server, rather than client */ |
85 |
|
|
int gethost:1; /* getting hostname -- not a real connection */ |
86 |
|
|
int dnrdone:1; /* DNR query is done */ |
87 |
|
|
short wbnum; /* write buffer for next write */ |
88 |
|
|
unsigned short wbsize; /* size of write buffer */ |
89 |
|
|
unsigned short reason; /* reason for TCP termination */ |
90 |
|
|
StreamPtr stream; /* TCP stream */ |
91 |
|
|
ip_port port; /* TCP port number to connect to */ |
92 |
|
|
void *context; /* user context */ |
93 |
|
|
na_tcpreadp *callback; /* callback function */ |
94 |
|
|
rdsEntry rrds[RDS+1]; /* read RDS structure */ |
95 |
|
|
tcpwb wb[2]; /* write buffers */ |
96 |
|
|
struct hostInfo host; /* hostname & ip_addr of host */ |
97 |
|
|
TCPiopb pb; /* parameter block for TCP connect/write/close */ |
98 |
|
|
TCPiopb rpb; /* parameter block for TCP read */ |
99 |
|
|
char buf[1]; /* stream buffer follows */ |
100 |
|
|
} tcpinfo; |
101 |
|
|
|
102 |
|
|
/* TCP task state information |
103 |
|
|
*/ |
104 |
|
|
struct tcpstate { |
105 |
|
|
na_win win; |
106 |
|
|
short tcp_driver; |
107 |
|
|
na_tcpinitp *tcp_initp; |
108 |
|
|
tcpinfo *tcpbufs[MAX_TCPCON]; |
109 |
|
|
IOParam *open_pb; |
110 |
|
|
long waiticks, waitstart; |
111 |
|
|
short waitpercent; |
112 |
|
|
long streamsize; |
113 |
|
|
byte TOS, precedence; |
114 |
|
|
unsigned short wbsize; |
115 |
|
|
char localhost[256]; |
116 |
|
|
} **tcpstate = NULL; |
117 |
|
|
#define tstate ((struct tcpstate *) win) |
118 |
|
|
|
119 |
|
|
/* tcp state bitmasks */ |
120 |
|
|
#define TCP_PBINUSE 0x04 /* tcp->pb in use */ |
121 |
|
|
#define TCP_DNSINUSE 0x08 /* DNS in use */ |
122 |
|
|
#define TCP_NOTREADY 0x10 /* not ready for reading */ |
123 |
|
|
/* tcp states */ |
124 |
|
|
#define TCP_READY 1 /* inactive */ |
125 |
|
|
#define TCP_RESOLVE (TCP_NOTREADY + TCP_DNSINUSE + 0) /* resolving hostname */ |
126 |
|
|
#define TCP_GETHOST (TCP_NOTREADY + TCP_DNSINUSE + 1) /* looking up hostname */ |
127 |
|
|
#define TCP_WRITING (TCP_PBINUSE + 0) /* writing data */ |
128 |
|
|
#define TCP_CONNECT (TCP_NOTREADY + TCP_PBINUSE + 0) /* waiting for a connection */ |
129 |
|
|
#define TCP_CLOSING (TCP_NOTREADY + TCP_PBINUSE + 1) /* waiting for TCPclose */ |
130 |
|
|
#define TCP_CLOSED 2 /* closed */ |
131 |
|
|
|
132 |
|
|
/* free write buffer storage |
133 |
|
|
*/ |
134 |
|
|
static void freewb(tcpwb *wb) |
135 |
|
|
{ |
136 |
|
|
short i; |
137 |
|
|
|
138 |
|
|
for (i = 0; wb->rds[i].length; ++i) { |
139 |
|
|
if (wb->fflag[i]) { |
140 |
|
|
DisposPtr(wb->rds[i].ptr); |
141 |
|
|
wb->fflag[i] = 0; |
142 |
|
|
} |
143 |
|
|
} |
144 |
|
|
wb->rused = 0; |
145 |
|
|
wb->wused = 0; |
146 |
|
|
} |
147 |
|
|
|
148 |
|
|
/* make sure tcp_driver is open |
149 |
|
|
*/ |
150 |
|
|
static short tcp_checkdriver() |
151 |
|
|
{ |
152 |
|
|
short msg = NATCP_nodriver; |
153 |
|
|
struct tcpstate *ts = *tcpstate; |
154 |
|
|
IOParam *opb = ts->open_pb; |
155 |
|
|
|
156 |
|
|
if (!opb) return (1); |
157 |
|
|
if (opb->ioResult == 1) return (0); |
158 |
|
|
if (opb->ioResult == noErr && OpenResolver(nil) == noErr) { |
159 |
|
|
ts->tcp_driver = opb->ioRefNum; |
160 |
|
|
msg = NATCP_connect; |
161 |
|
|
} |
162 |
|
|
DisposPtr((Ptr) opb); |
163 |
|
|
ts = *tcpstate; |
164 |
|
|
ts->open_pb = NULL; |
165 |
|
|
(*ts->tcp_initp)(msg); |
166 |
|
|
|
167 |
|
|
return (1); |
168 |
|
|
} |
169 |
|
|
|
170 |
|
|
/* wait for MacTCP to finish whatever it's doing, with user cancel |
171 |
|
|
*/ |
172 |
|
|
static short tcp_wait(struct tcpstate *ts, tcpinfo *tcp) |
173 |
|
|
{ |
174 |
|
|
KeyMap mapkeys; |
175 |
|
|
#define keys ((unsigned char *)mapkeys) |
176 |
|
|
short percent; |
177 |
|
|
|
178 |
|
|
while (!tcp_checkdriver() |
179 |
|
|
|| (tcp && (tcp->state & TCP_DNSINUSE) && ! (volatile) tcp->dnrdone) |
180 |
|
|
|| (tcp && (tcp->state & TCP_PBINUSE) && (volatile short) tcp->pb.ioResult == 1)) { |
181 |
|
|
if (ts) { |
182 |
|
|
if (!ts->waiticks) return (0); |
183 |
|
|
percent = ((TickCount() - ts->waitstart) * 100) / ts->waiticks; |
184 |
|
|
if (percent > 100) percent = 100; |
185 |
|
|
if (percent != ts->waitpercent) { |
186 |
|
|
(*ts->tcp_initp)(ts->waitpercent = percent); |
187 |
|
|
} |
188 |
|
|
if (percent == 100) return (0); |
189 |
|
|
} |
190 |
|
|
SystemTask(); |
191 |
|
|
GetKeys(mapkeys); |
192 |
|
|
if ((keys[0x37 >> 3] >> (0x37 & 7)) |
193 |
|
|
& (keys[0x2f >> 3] >> (0x2f & 7)) & 1) { |
194 |
|
|
return (0); |
195 |
|
|
} |
196 |
|
|
} |
197 |
|
|
|
198 |
|
|
return (1); |
199 |
|
|
} |
200 |
|
|
|
201 |
|
|
/* clean up the TCP task |
202 |
|
|
*/ |
203 |
|
|
static short tcp_closep(na_win *win) |
204 |
|
|
{ |
205 |
|
|
short i, j; |
206 |
|
|
tcpinfo *tcp; |
207 |
|
|
tcpwb *wb; |
208 |
|
|
TCPiopb pb; |
209 |
|
|
|
210 |
|
|
tstate->waitstart = TickCount(); |
211 |
|
|
tstate->waitpercent = 0; |
212 |
|
|
tcp_wait(tstate, NULL); |
213 |
|
|
memset((void *) &pb, 0, sizeof (pb)); |
214 |
|
|
for (i = 0; i < MAX_TCPCON; ++i) { |
215 |
|
|
if ((tcp = tstate->tcpbufs[i]) != NULL) { |
216 |
|
|
/* wait for MacTCP to finish what it's doing, but permit user cancel */ |
217 |
|
|
if (!tcp->server || tcp->state != TCP_CONNECT) tcp_wait(tstate, tcp); |
218 |
|
|
if (!tcp->gethost) { |
219 |
|
|
pb.ioCRefNum = tstate->tcp_driver; |
220 |
|
|
pb.tcpStream = tcp->stream; |
221 |
|
|
pb.csCode = TCPRelease; |
222 |
|
|
PBControl((ParmBlkPtr) &pb, false); |
223 |
|
|
} |
224 |
|
|
freewb(tcp->wb); |
225 |
|
|
freewb(tcp->wb + 1); |
226 |
|
|
DisposPtr((Ptr) tcp); |
227 |
|
|
tstate->tcpbufs[i] = NULL; |
228 |
|
|
} |
229 |
|
|
} |
230 |
|
|
tcpstate = NULL; |
231 |
|
|
if (tstate->tcp_driver) CloseResolver(); |
232 |
|
|
|
233 |
|
|
return (NA_CLOSED); |
234 |
|
|
} |
235 |
|
|
|
236 |
|
|
/* begin writing data |
237 |
|
|
*/ |
238 |
|
|
static short beginwrite(tcpinfo *tcp) |
239 |
|
|
{ |
240 |
|
|
tcpwb *wb = tcp->wb + tcp->wbnum; |
241 |
|
|
|
242 |
|
|
/* if connection terminated, or we've sent a TCPclose, we can't write */ |
243 |
|
|
if (tcp->rclose == 3 || tcp->lclose > 1) return (0); |
244 |
|
|
memset((void *) &tcp->pb.csParam, 0, sizeof (tcp->pb.csParam)); |
245 |
|
|
tcp->pb.csCode = TCPSend; |
246 |
|
|
tcp->pb.csParam.send.ulpTimeoutValue = 60; /* 1 minute to send data */ |
247 |
|
|
tcp->pb.csParam.send.ulpTimeoutAction = 0; |
248 |
|
|
tcp->pb.csParam.send.validityFlags = 0xC0; |
249 |
|
|
tcp->pb.csParam.send.wdsPtr = (Ptr) wb->rds; |
250 |
|
|
tcp->pb.csParam.send.pushFlag = tcp->pushed = tcp->push; |
251 |
|
|
PBControl((ParmBlkPtr) &tcp->pb, true); |
252 |
|
|
tcp->push = 0; |
253 |
|
|
tcp->wbnum = 1 - tcp->wbnum; |
254 |
|
|
wb->rused = -1; |
255 |
|
|
|
256 |
|
|
return (tcp->state = TCP_WRITING); |
257 |
|
|
} |
258 |
|
|
|
259 |
|
|
/* do I/O polling |
260 |
|
|
*/ |
261 |
|
|
short NATCPtask(na_win *win) |
262 |
|
|
{ |
263 |
|
|
tcpinfo *tcp; |
264 |
|
|
rdsEntry *rds, *trds; |
265 |
|
|
short result, newstate; |
266 |
|
|
short processed = NA_PROCESSED; |
267 |
|
|
na_tcp i; |
268 |
|
|
tcpwb *wb; |
269 |
|
|
int j; |
270 |
|
|
|
271 |
|
|
/* finish off driver initialization: */ |
272 |
|
|
if (!tstate->tcp_driver) { |
273 |
|
|
if (!tcp_checkdriver()) return (NA_NOTPROCESSED); |
274 |
|
|
if (!tstate->tcp_driver) return (NA_REQCLOSE); |
275 |
|
|
} |
276 |
|
|
/* loop through connections */ |
277 |
|
|
for (i = 0; i < MAX_TCPCON; ++i) { |
278 |
|
|
if ((tcp = tstate->tcpbufs[i]) != NULL) do { |
279 |
|
|
/* read data if we have it */ |
280 |
|
|
if (!tcp->reading && !tcp->rclose && tcp->havedata && tcp->state != TCP_CONNECT) { |
281 |
|
|
tcp->rpb.ioCRefNum = tstate->tcp_driver; |
282 |
|
|
tcp->rpb.tcpStream = tcp->stream; |
283 |
|
|
tcp->rpb.csCode = TCPNoCopyRcv; |
284 |
|
|
tcp->rpb.csParam.receive.rdsPtr = (Ptr) tcp->rrds; |
285 |
|
|
tcp->rpb.csParam.receive.commandTimeoutValue = 5; |
286 |
|
|
tcp->rpb.csParam.receive.rdsLength = RDS; |
287 |
|
|
if (tcp->pushed) { |
288 |
|
|
tcp->rpb.csParam.receive.commandTimeoutValue = 1; |
289 |
|
|
tcp->rpb.csParam.receive.rdsLength = 1; |
290 |
|
|
} |
291 |
|
|
tcp->havedata = 0; |
292 |
|
|
PBControl((ParmBlkPtr) &tcp->rpb, tcp->pushed ? false : true); |
293 |
|
|
tcp->reading = 1; |
294 |
|
|
tcp->pushed = 0; |
295 |
|
|
} |
296 |
|
|
if (tcp->reading) { |
297 |
|
|
if ((result = tcp->rpb.ioResult) == 1) { |
298 |
|
|
processed = NA_NOTPROCESSED; |
299 |
|
|
} else { |
300 |
|
|
tcp->reading = 0; |
301 |
|
|
if (result != noErr) { |
302 |
|
|
if (result != commandTimeout) { |
303 |
|
|
(*tcp->callback)(tcp->context, i, NATCP_noread, result, NULL); |
304 |
|
|
} |
305 |
|
|
} else { |
306 |
|
|
result = NATCP_data | NATCP_more; |
307 |
|
|
if (tcp->rpb.csParam.receive.urgentFlag) tcp->urgent = 1; |
308 |
|
|
if (tcp->urgent) result |= NATCP_urgent; |
309 |
|
|
if (tcp->rpb.csParam.receive.markFlag) tcp->urgent = 0; |
310 |
|
|
for (rds = tcp->rrds; rds->length; ++rds) { |
311 |
|
|
if (!rds[1].length) result &= ~NATCP_more; |
312 |
|
|
(*tcp->callback)(tcp->context, i, result, |
313 |
|
|
rds->length, rds->ptr); |
314 |
|
|
} |
315 |
|
|
tcp->rpb.csCode = TCPRcvBfrReturn; |
316 |
|
|
PBControl((ParmBlkPtr) &tcp->rpb, false); |
317 |
|
|
} |
318 |
|
|
} |
319 |
|
|
} |
320 |
|
|
result = tcp->pb.ioResult; |
321 |
|
|
newstate = 0; |
322 |
|
|
switch (tcp->state) { |
323 |
|
|
case TCP_GETHOST: |
324 |
|
|
if (tcp->dnrdone) { |
325 |
|
|
tcp->rclose = 3; |
326 |
|
|
newstate = TCP_CLOSED; |
327 |
|
|
if (tcp->host.rtnCode != noErr) { |
328 |
|
|
(*tcp->callback)(tcp->context, i, NATCP_nohost, |
329 |
|
|
tcp->host.rtnCode, NULL); |
330 |
|
|
} else { |
331 |
|
|
(*tcp->callback)(tcp->context, i, NATCP_connect, |
332 |
|
|
strlen(tcp->host.cname), tcp->host.cname); |
333 |
|
|
strcpy(tstate->localhost, tcp->host.cname); |
334 |
|
|
} |
335 |
|
|
} |
336 |
|
|
break; |
337 |
|
|
case TCP_RESOLVE: |
338 |
|
|
if (tcp->dnrdone) { |
339 |
|
|
if (tcp->host.rtnCode != noErr) { |
340 |
|
|
tcp->rclose = 3; |
341 |
|
|
newstate = TCP_CLOSED; |
342 |
|
|
(*tcp->callback)(tcp->context, i, NATCP_nohost, |
343 |
|
|
tcp->host.rtnCode, NULL); |
344 |
|
|
} else if (!tcp->lclose) { |
345 |
|
|
memset((void *) &tcp->pb, 0, sizeof (tcp->pb)); |
346 |
|
|
tcp->pb.ioCRefNum = tstate->tcp_driver; |
347 |
|
|
tcp->pb.tcpStream = tcp->stream; |
348 |
|
|
tcp->pb.csParam.open.ulpTimeoutValue = 30; |
349 |
|
|
tcp->pb.csParam.open.ulpTimeoutAction = 1; /* Abort on timeout */ |
350 |
|
|
tcp->pb.csParam.open.tosFlags = tstate->TOS; |
351 |
|
|
tcp->pb.csParam.open.precedence = tstate->precedence; |
352 |
|
|
tcp->pb.csParam.open.validityFlags = |
353 |
|
|
timeoutValue|timeoutAction|typeOfService|precedence; |
354 |
|
|
tcp->pb.csParam.open.remoteHost = tcp->host.addr[0]; |
355 |
|
|
if (tcp->server) { |
356 |
|
|
tcp->pb.csCode = TCPPassiveOpen; |
357 |
|
|
tcp->pb.csParam.open.commandTimeoutValue = 0; |
358 |
|
|
tcp->pb.csParam.open.remotePort = 0; |
359 |
|
|
tcp->pb.csParam.open.localPort = tcp->port; |
360 |
|
|
} else { |
361 |
|
|
tcp->pb.csCode = TCPActiveOpen; |
362 |
|
|
tcp->pb.csParam.open.remotePort = tcp->port; |
363 |
|
|
tcp->pb.csParam.open.localPort = 0; |
364 |
|
|
} |
365 |
|
|
PBControl((ParmBlkPtr) &tcp->pb, true); |
366 |
|
|
newstate = TCP_CONNECT; |
367 |
|
|
} |
368 |
|
|
} |
369 |
|
|
break; |
370 |
|
|
case TCP_CONNECT: |
371 |
|
|
if (result == 1) { |
372 |
|
|
processed = NA_NOTPROCESSED; |
373 |
|
|
break; |
374 |
|
|
} |
375 |
|
|
if (result != noErr) { |
376 |
|
|
tcp->rclose = 3; |
377 |
|
|
newstate = TCP_CLOSED; |
378 |
|
|
(*tcp->callback)(tcp->context, i, NATCP_nocon, result, NULL); |
379 |
|
|
} else { |
380 |
|
|
newstate = TCP_READY; |
381 |
|
|
if (tcp->server) { |
382 |
|
|
tcp->port = tcp->pb.csParam.open.remotePort; |
383 |
|
|
if (!*tcp->host.cname) { |
384 |
|
|
AddrToStr(tcp->pb.csParam.open.remoteHost, tcp->host.cname); |
385 |
|
|
} |
386 |
|
|
} |
387 |
|
|
(*tcp->callback)(tcp->context, i, NATCP_connect, |
388 |
|
|
tcp->port, tcp->host.cname); |
389 |
|
|
} |
390 |
|
|
break; |
391 |
|
|
case TCP_READY: |
392 |
|
|
/* Write data if we have it */ |
393 |
|
|
wb = tcp->wb + tcp->wbnum; |
394 |
|
|
if (wb->rused && (newstate = beginwrite(tcp))) { |
395 |
|
|
break; |
396 |
|
|
} |
397 |
|
|
/* check if other side wants to close */ |
398 |
|
|
if (tcp->rclose == 1) { |
399 |
|
|
tcp->rclose = 2; |
400 |
|
|
(*tcp->callback)(tcp->context, i, NATCP_closing, 0, NULL); |
401 |
|
|
} |
402 |
|
|
/* check if connection needs closing at this end */ |
403 |
|
|
if (tcp->lclose == 1) { |
404 |
|
|
tcp->lclose = 2; |
405 |
|
|
tcp->pb.csCode = TCPClose; |
406 |
|
|
tcp->pb.csParam.close.validityFlags = 0xC0; |
407 |
|
|
tcp->pb.csParam.close.ulpTimeoutValue = 30; /* give 30 secs to close */ |
408 |
|
|
tcp->pb.csParam.close.ulpTimeoutAction = 0; |
409 |
|
|
PBControl((ParmBlkPtr) &tcp->pb, true); |
410 |
|
|
newstate = TCP_CLOSING; |
411 |
|
|
break; |
412 |
|
|
} |
413 |
|
|
/* check if connection closed at both ends */ |
414 |
|
|
if (tcp->rclose == 3) { |
415 |
|
|
(*tcp->callback)(tcp->context, i, NATCP_closed, tcp->reason, NULL); |
416 |
|
|
newstate = TCP_CLOSED; |
417 |
|
|
} |
418 |
|
|
break; |
419 |
|
|
case TCP_WRITING: |
420 |
|
|
if (result == 1) { |
421 |
|
|
processed = NA_NOTPROCESSED; |
422 |
|
|
break; |
423 |
|
|
} |
424 |
|
|
wb = tcp->wb; |
425 |
|
|
if (wb->rused != -1) ++wb; |
426 |
|
|
freewb(wb); |
427 |
|
|
if (result != noErr) { |
428 |
|
|
tcp->pushed = 0; |
429 |
|
|
(*tcp->callback)(tcp->context, i, NATCP_nowrite, result, NULL); |
430 |
|
|
} |
431 |
|
|
newstate = TCP_READY; |
432 |
|
|
break; |
433 |
|
|
case TCP_CLOSING: |
434 |
|
|
if (result == 1) { |
435 |
|
|
processed = NA_NOTPROCESSED; |
436 |
|
|
break; |
437 |
|
|
} |
438 |
|
|
newstate = TCP_READY; |
439 |
|
|
break; |
440 |
|
|
case TCP_CLOSED: |
441 |
|
|
if (!tcp->rclose) break; |
442 |
|
|
if (!tcp->gethost) { |
443 |
|
|
tcp->pb.csCode = TCPRelease; |
444 |
|
|
PBControl((ParmBlkPtr)&tcp->pb, false); |
445 |
|
|
} |
446 |
|
|
freewb(tcp->wb); |
447 |
|
|
freewb(tcp->wb + 1); |
448 |
|
|
DisposPtr((Ptr) tcp); |
449 |
|
|
tstate->tcpbufs[i] = NULL; |
450 |
|
|
break; |
451 |
|
|
} |
452 |
|
|
if (newstate) tcp->state = newstate; |
453 |
|
|
} while (newstate); |
454 |
|
|
} |
455 |
|
|
|
456 |
|
|
return (processed); |
457 |
|
|
} |
458 |
|
|
|
459 |
|
|
/* Async notification routine |
460 |
|
|
*/ |
461 |
|
|
static pascal void myTCPNotifyProc(StreamPtr stream, unsigned short eventCode, |
462 |
|
|
Ptr userDataPtr, unsigned short terminReason, struct ICMPReport *icmpMsg) |
463 |
|
|
{ |
464 |
|
|
tcpinfo *tcp = (tcpinfo *) userDataPtr; |
465 |
|
|
|
466 |
|
|
switch (eventCode) { |
467 |
|
|
case TCPTerminate: |
468 |
|
|
tcp->rclose = 3; |
469 |
|
|
tcp->reason = terminReason; |
470 |
|
|
break; |
471 |
|
|
case TCPClosing: |
472 |
|
|
tcp->rclose = 1; |
473 |
|
|
break; |
474 |
|
|
case TCPDataArrival: |
475 |
|
|
tcp->havedata = 1; |
476 |
|
|
break; |
477 |
|
|
case TCPUrgent: |
478 |
|
|
tcp->urgent = 1; |
479 |
|
|
break; |
480 |
|
|
} |
481 |
|
|
} |
482 |
|
|
|
483 |
|
|
/* DNR resultproc */ |
484 |
|
|
static pascal void addrproc(struct hostInfo *hinfop, char *udata) |
485 |
|
|
{ |
486 |
|
|
((tcpinfo *) udata)->dnrdone = 1; |
487 |
|
|
} |
488 |
|
|
|
489 |
|
|
/* callback to pass TCP info to window |
490 |
|
|
*/ |
491 |
|
|
static void winreadp(void *context, na_tcp i, short status, long len, char *buf) |
492 |
|
|
{ |
493 |
|
|
natcp_win *w; |
494 |
|
|
|
495 |
|
|
w = (natcp_win *) NAlockWindow((na_win **) context); |
496 |
|
|
w->s = i; |
497 |
|
|
(*w->readp)(&w->winp, status, len, buf); |
498 |
|
|
NAunlockWindowh((na_win **) context, &w->winp); |
499 |
|
|
} |
500 |
|
|
|
501 |
|
|
/* adjust TCP settings |
502 |
|
|
*/ |
503 |
|
|
void NATCPsettings(long streamsize, short type_of_service, short precedence, unsigned short wbsize) |
504 |
|
|
{ |
505 |
|
|
if (!streamsize) streamsize = DEF_STREAM_SIZE; |
506 |
|
|
(*tcpstate)->streamsize = streamsize ? streamsize : DEF_STREAM_SIZE; |
507 |
|
|
(*tcpstate)->TOS = type_of_service; |
508 |
|
|
(*tcpstate)->precedence = precedence; |
509 |
|
|
if (!wbsize) wbsize = 1024; |
510 |
|
|
(*tcpstate)->wbsize = wbsize; |
511 |
|
|
} |
512 |
|
|
|
513 |
|
|
/* initialize TCP system |
514 |
|
|
*/ |
515 |
|
|
void NATCPinit(na_tcpinitp *initp) |
516 |
|
|
{ |
517 |
|
|
IOParam *pb; |
518 |
|
|
int i; |
519 |
|
|
struct tcpstate *ts; |
520 |
|
|
|
521 |
|
|
pb = (IOParam *) NewPtrClear(sizeof (IOParam)); |
522 |
|
|
tcpstate = (struct tcpstate **) NAaddtask(NATCPtask, sizeof (struct tcpstate)); |
523 |
|
|
if (!tcpstate || !pb) { |
524 |
|
|
(*initp)(NATCP_nomem); |
525 |
|
|
} else { |
526 |
|
|
pb->ioNamePtr = "\p.IPP"; |
527 |
|
|
PBOpen((ParmBlkPtr) pb, true); |
528 |
|
|
ts = *tcpstate; |
529 |
|
|
for (i = 0; i < MAX_TCPCON; ++i) ts->tcpbufs[i] = NULL; |
530 |
|
|
ts->waiticks = 60; /* wait 1 sec for TCP close by default */ |
531 |
|
|
ts->win.type = NA_TCPTYPE; |
532 |
|
|
ts->win.closep = tcp_closep; |
533 |
|
|
ts->win.priority = -1; |
534 |
|
|
ts->tcp_initp = initp; |
535 |
|
|
ts->open_pb = pb; |
536 |
|
|
NATCPsettings(0, 0, 0, 0); |
537 |
|
|
} |
538 |
|
|
} |
539 |
|
|
|
540 |
|
|
/* get a TCP buffer block |
541 |
|
|
*/ |
542 |
|
|
static tcpinfo *getTCPbuf(na_tcpreadp *callback, void *context, int *id) |
543 |
|
|
{ |
544 |
|
|
int i; |
545 |
|
|
tcpinfo *tcp; |
546 |
|
|
|
547 |
|
|
/* make sure driver is open */ |
548 |
|
|
if (!(*tcpstate)->tcp_driver |
549 |
|
|
&& (!tcp_wait(NULL, NULL) || !(*tcpstate)->tcp_driver)) { |
550 |
|
|
(*callback)(context, -1, NATCP_nodriver, 0, NULL); |
551 |
|
|
return (NULL); |
552 |
|
|
} |
553 |
|
|
|
554 |
|
|
/* find pointer slot and create buffer */ |
555 |
|
|
for (i = 0; i < MAX_TCPCON && (*tcpstate)->tcpbufs[i]; ++i); |
556 |
|
|
if (i == MAX_TCPCON) { |
557 |
|
|
(*callback)(context, -1, NATCP_notcpbuf, 0, NULL); |
558 |
|
|
return (NULL); |
559 |
|
|
} |
560 |
|
|
tcp = (tcpinfo *) NewPtr(sizeof (tcpinfo) - 1 |
561 |
|
|
+ (*tcpstate)->streamsize + (unsigned long) (*tcpstate)->wbsize * 2); |
562 |
|
|
if (!tcp) { |
563 |
|
|
(*callback)(context, -1, NATCP_nomem, 0, NULL); |
564 |
|
|
return (NULL); |
565 |
|
|
}; |
566 |
|
|
*id = i; |
567 |
|
|
(*tcpstate)->tcpbufs[i] = tcp; |
568 |
|
|
memset((char *) tcp, 0, sizeof (tcpinfo)); |
569 |
|
|
|
570 |
|
|
/* initialize fields from global state */ |
571 |
|
|
tcp->wbsize = (*tcpstate)->wbsize; |
572 |
|
|
tcp->wb[0].buf = tcp->buf + (*tcpstate)->streamsize; |
573 |
|
|
tcp->wb[1].buf = tcp->wb[0].buf + tcp->wbsize; |
574 |
|
|
tcp->pb.ioCRefNum = (*tcpstate)->tcp_driver; |
575 |
|
|
tcp->context = context; |
576 |
|
|
tcp->callback = callback; |
577 |
|
|
|
578 |
|
|
return (tcp); |
579 |
|
|
} |
580 |
|
|
|
581 |
|
|
/* get host name |
582 |
|
|
*/ |
583 |
|
|
void NATCPgethost(na_tcpreadp *callback, void *context) |
584 |
|
|
{ |
585 |
|
|
int id; |
586 |
|
|
tcpinfo *tcp; |
587 |
|
|
struct IPParamBlock *ippb; |
588 |
|
|
na_win *win; |
589 |
|
|
|
590 |
|
|
if ((*tcpstate)->localhost[0]) { |
591 |
|
|
win = NAlockWindow((na_win **) tcpstate); |
592 |
|
|
(*callback)(context, -1, NATCP_connect, strlen(tstate->localhost), |
593 |
|
|
tstate->localhost); |
594 |
|
|
NAunlockWindowh((na_win **) tcpstate, win); |
595 |
|
|
} else if ((tcp = getTCPbuf(callback, context, &id)) != NULL) { |
596 |
|
|
/* here we make the assumption that an IP param block is smaller than |
597 |
|
|
* a TCP param block. This seems like a safe assumption to me. |
598 |
|
|
*/ |
599 |
|
|
ippb = (struct IPParamBlock *) &tcp->pb; |
600 |
|
|
/* get IP address */ |
601 |
|
|
ippb->ioCRefNum = (*tcpstate)->tcp_driver; |
602 |
|
|
ippb->csCode = ipctlGetAddr; |
603 |
|
|
PBControl((ParmBlkPtr)ippb, false); |
604 |
|
|
if (ippb->ioResult != 0) { |
605 |
|
|
(*callback)(context, -1, NATCP_notcpbuf, ippb->ioResult, NULL); |
606 |
|
|
DisposPtr((Ptr) tcp); |
607 |
|
|
(*tcpstate)->tcpbufs[id] = NULL; |
608 |
|
|
} else { |
609 |
|
|
/* begin IP address lookup */ |
610 |
|
|
tcp->dnrdone = 0; |
611 |
|
|
AddrToName(ippb->ourAddress, &tcp->host, addrproc, (char *) tcp); |
612 |
|
|
tcp->state = TCP_GETHOST; |
613 |
|
|
tcp->gethost = 1; |
614 |
|
|
} |
615 |
|
|
} |
616 |
|
|
} |
617 |
|
|
|
618 |
|
|
/* open a TCP connection |
619 |
|
|
*/ |
620 |
|
|
na_tcp NATCPopen(na_tcpreadp *callback, void *context, char *host, long port, short flags) |
621 |
|
|
{ |
622 |
|
|
int i, err = NATCP_notcpbuf; |
623 |
|
|
OSErr resolve = noErr; |
624 |
|
|
tcpinfo *tcp; |
625 |
|
|
|
626 |
|
|
if ((tcp = getTCPbuf(callback, context, &i)) == NULL) return (-1); |
627 |
|
|
if (flags & NATCP_server) tcp->server = 1; |
628 |
|
|
tcp->port = port; |
629 |
|
|
tcp->pb.csCode = TCPCreate; |
630 |
|
|
tcp->pb.csParam.create.rcvBuff = (Ptr) tcp->buf; |
631 |
|
|
tcp->pb.csParam.create.rcvBuffLen = (*tcpstate)->streamsize; |
632 |
|
|
tcp->pb.csParam.create.notifyProc = myTCPNotifyProc; |
633 |
|
|
tcp->pb.csParam.create.userDataPtr = (Ptr) tcp; |
634 |
|
|
PBControl((ParmBlkPtr)&tcp->pb, false); |
635 |
|
|
if (tcp->pb.ioResult == 0) { |
636 |
|
|
tcp->state = TCP_RESOLVE; |
637 |
|
|
tcp->stream = tcp->pb.tcpStream; |
638 |
|
|
/* a server isn't required to have a hostname */ |
639 |
|
|
if (!host && tcp->server) return (i); |
640 |
|
|
tcp->dnrdone = 0; |
641 |
|
|
resolve = StrToAddr(host, &tcp->host, addrproc, (char *) tcp); |
642 |
|
|
if (resolve == noErr) tcp->dnrdone = 1; |
643 |
|
|
if (resolve == cacheFault || resolve == noErr) { |
644 |
|
|
return (i); |
645 |
|
|
} |
646 |
|
|
err = NATCP_nohost; |
647 |
|
|
} |
648 |
|
|
DisposPtr((Ptr) tcp); |
649 |
|
|
(*tcpstate)->tcpbufs[i] = NULL; |
650 |
|
|
(*callback)(context, -1, err, resolve, NULL); |
651 |
|
|
|
652 |
|
|
return (-1); |
653 |
|
|
} |
654 |
|
|
|
655 |
|
|
/* open a connection to a tcp window |
656 |
|
|
*/ |
657 |
|
|
na_tcp NATCPwinopen(natcp_win *w, char *host, long port, short flags) |
658 |
|
|
{ |
659 |
|
|
return (NATCPopen(winreadp, (void *) RecoverHandle((Ptr) w), host, port, flags)); |
660 |
|
|
} |
661 |
|
|
|
662 |
|
|
/* pass a buffer to tcp connection for writing |
663 |
|
|
* dispose of -1 = copy data |
664 |
|
|
*/ |
665 |
|
|
short NATCPwrite(na_tcp i, Ptr data, long len, short dispose) |
666 |
|
|
{ |
667 |
|
|
tcpinfo *tcp = (*tcpstate)->tcpbufs[i]; |
668 |
|
|
rdsEntry *rds = NULL; |
669 |
|
|
tcpwb *wb; |
670 |
|
|
long totallen = 0; |
671 |
|
|
int j; |
672 |
|
|
|
673 |
|
|
if (tcp == NULL || tcp->lclose > 0 || tcp->rclose == 3) { |
674 |
|
|
return (NATCP_nocon); |
675 |
|
|
} |
676 |
|
|
wb = tcp->wb + tcp->wbnum; |
677 |
|
|
if (wb->rused == RDS) wb = tcp->wb + (1 - tcp->wbnum); |
678 |
|
|
if (wb->rused == -1 || wb->rused == RDS) return (NATCP_notcpbuf); |
679 |
|
|
for (j = 0; j < wb->rused; ++j) { |
680 |
|
|
totallen += wb->rds[j].length; |
681 |
|
|
} |
682 |
|
|
if (totallen + len >= 65535) return (NATCP_notcpbuf); |
683 |
|
|
rds = wb->rds + wb->rused; |
684 |
|
|
rds->length = len; |
685 |
|
|
rds->ptr = data; |
686 |
|
|
rds[1].length = 0; |
687 |
|
|
if (dispose < 0) { |
688 |
|
|
if (len < tcp->wbsize - wb->wused) { |
689 |
|
|
/* if data short enough, use small internal buffer */ |
690 |
|
|
rds->ptr = wb->buf + wb->wused; |
691 |
|
|
wb->wused += len; |
692 |
|
|
dispose = 0; |
693 |
|
|
/* If adjacent to last rds, attach to it */ |
694 |
|
|
if (wb->rused && rds[-1].ptr + rds[-1].length == rds->ptr) { |
695 |
|
|
--wb->rused; |
696 |
|
|
rds[-1].length += len; |
697 |
|
|
rds->length = 0; |
698 |
|
|
} |
699 |
|
|
} else { |
700 |
|
|
rds->ptr = NewPtr(len); |
701 |
|
|
if (!rds->ptr) return (NATCP_nomem); |
702 |
|
|
dispose = 1; |
703 |
|
|
} |
704 |
|
|
memcpy(rds->ptr, data, len); |
705 |
|
|
} |
706 |
|
|
wb->fflag[wb->rused++] = dispose; |
707 |
|
|
if (tcp->push && tcp->state == TCP_READY) { |
708 |
|
|
(void) beginwrite(tcp); |
709 |
|
|
} |
710 |
|
|
|
711 |
|
|
return (NATCP_data); |
712 |
|
|
} |
713 |
|
|
|
714 |
|
|
/* put a character on the TCP connection -- optimized for fast turnaround |
715 |
|
|
*/ |
716 |
|
|
short NATCPputchar(na_tcp i, char c) |
717 |
|
|
{ |
718 |
|
|
(*tcpstate)->tcpbufs[i]->push = 1; |
719 |
|
|
|
720 |
|
|
return (NATCPwrite(i, (Ptr) &c, 1, -1)); |
721 |
|
|
} |
722 |
|
|
|
723 |
|
|
/* close a TCP connection |
724 |
|
|
*/ |
725 |
|
|
void NATCPclose(na_tcp i) |
726 |
|
|
{ |
727 |
|
|
tcpinfo *tcp = (*tcpstate)->tcpbufs[i]; |
728 |
|
|
|
729 |
|
|
if (tcp && tcp->lclose < 1) tcp->lclose = 1; |
730 |
|
|
} |
731 |
|
|
|
732 |
|
|
/* dispose of all TCP system resources |
733 |
|
|
*/ |
734 |
|
|
void NATCPdone(long waiticks) |
735 |
|
|
{ |
736 |
|
|
struct tcpstate *ts; |
737 |
|
|
|
738 |
|
|
if (tcpstate) { |
739 |
|
|
ts = (struct tcpstate *) NAlockWindow((na_win **) tcpstate); |
740 |
|
|
ts->waiticks = waiticks; |
741 |
|
|
NAcloseWindow((na_win *) ts, NA_REQCLOSE); |
742 |
|
|
} |
743 |
|
|
} |