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 |
} |