1 /* ========================================================================
2 * Copyright 1988-2008 University of Washington
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *
11 * ========================================================================
12 */
13
14 /*
15 * Program: Macintosh TCP/IP routines
16 *
17 * Author: Mark Crispin
18 * Networks and Distributed Computing
19 * Computing & Communications
20 * University of Washington
21 * Administration Building, AG-44
22 * Seattle, WA 98195
23 * Internet: MRC@CAC.Washington.EDU
24 *
25 * Date: 26 January 1992
26 * Last Edited: 13 January 2008
27 */
28
29
30 /* This is a totally new operating-system dependent module for the Macintosh,
31 * written using THINK C on my Mac PowerBook-100 in my free time.
32 * Unlike earlier efforts, this version requires no external TCP library. It
33 * also takes advantage of the Map panel in System 7 for the timezone.
34 */
35
36 static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
37 static long ttmo_open = 75; /* TCP timeouts, in seconds */
38 static long ttmo_read = 0;
39 static long ttmo_write = 0;
40 static long ttmo_close = 0;
41
42 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
43 long *contd);
44
45 /* TCP/IP manipulate parameters
46 * Accepts: function code
47 * function-dependent value
48 * Returns: function-dependent return value
49 */
50
tcp_parameters(long function,void * value)51 void *tcp_parameters (long function,void *value)
52 {
53 void *ret = NIL;
54 switch ((int) function) {
55 case SET_TIMEOUT:
56 tmoh = (tcptimeout_t) value;
57 case GET_TIMEOUT:
58 ret = (void *) tmoh;
59 break;
60 case SET_OPENTIMEOUT:
61 ttmo_open = (long) value;
62 case GET_OPENTIMEOUT:
63 ret = (void *) ttmo_open;
64 break;
65 case SET_READTIMEOUT:
66 ttmo_read = (long) value;
67 case GET_READTIMEOUT:
68 ret = (void *) ttmo_read;
69 break;
70 case SET_WRITETIMEOUT:
71 ttmo_write = (long) value;
72 case GET_WRITETIMEOUT:
73 ret = (void *) ttmo_write;
74 break;
75 case SET_CLOSETIMEOUT:
76 ttmo_close = (long) value;
77 case GET_CLOSETIMEOUT:
78 ret = (void *) ttmo_close;
79 break;
80 }
81 return ret;
82 }
83
84 /* TCP/IP open
85 * Accepts: host name
86 * contact service name
87 * contact port number
88 * Returns: TCP stream if success else NIL
89 */
90
tcp_open(char * host,char * service,unsigned long port)91 TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
92 {
93 TCPSTREAM *stream;
94 struct hostInfo hst;
95 struct TCPCreatePB *createpb;
96 struct TCPOpenPB *openpb;
97 char *s;
98 unsigned long i,j,k,l;
99 char tmp[MAILTMPLEN];
100 port &= 0xffff; /* erase flags */
101 /* init MacTCP */
102 if (!TCPdriver && OpenDriver (TCPDRIVER,&TCPdriver)) {
103 mm_log ("Can't init MacTCP",ERROR);
104 return NIL;
105 }
106 if (!resolveropen && OpenResolver (NIL)) {
107 mm_log ("Can't init domain resolver",ERROR);
108 return NIL;
109 }
110 resolveropen = T; /* note resolver open now */
111 /* domain literal? */
112 if (host[0] == '[' && host[strlen (host)-1] == ']') {
113 if (((i = strtoul (s = host+1,&s,10)) <= 255) && *s++ == '.' &&
114 ((j = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
115 ((k = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
116 ((l = strtoul (s,&s,10)) <= 255) && *s++ == ']' && !*s) {
117 hst.addr[0] = (i << 24) + (j << 16) + (k << 8) + l;
118 hst.addr[1] = 0; /* only one address to try! */
119 sprintf (hst.cname,"[%ld.%ld.%ld.%ld]",i,j,k,l);
120 }
121 else {
122 sprintf (tmp,"Bad format domain-literal: %.80s",host);
123 mm_log (tmp,ERROR);
124 return NIL;
125 }
126 }
127
128 else { /* look up host name */
129 if (!tcp_dns_upp) tcp_dns_upp = NewResultProc (tcp_dns_result);
130 if (StrToAddr (host,&hst,tcp_dns_upp,NIL)) {
131 while (hst.rtnCode == cacheFault && wait ());
132 /* kludge around MacTCP bug */
133 if (hst.rtnCode == outOfMemory) {
134 mm_log ("Re-initializing domain resolver",WARN);
135 CloseResolver (); /* bop it on the head and try again */
136 OpenResolver (NIL); /* note this will leak 12K */
137 StrToAddr (host,&hst,tcp_dns_upp,NIL);
138 while (hst.rtnCode == cacheFault && wait ());
139 }
140 if (hst.rtnCode) { /* still have error status? */
141 switch (hst.rtnCode) { /* analyze return */
142 case nameSyntaxErr:
143 s = "Syntax error in name";
144 break;
145 case noResultProc:
146 s = "No result procedure";
147 break;
148 case noNameServer:
149 s = "No name server found";
150 break;
151 case authNameErr:
152 s = "Host does not exist";
153 break;
154 case noAnsErr:
155 s = "No name servers responding";
156 break;
157 case dnrErr:
158 s = "Name server returned an error";
159 break;
160 case outOfMemory:
161 s = "Not enough memory to resolve name";
162 break;
163 case notOpenErr:
164 s = "Driver not open";
165 break;
166 default:
167 s = NIL;
168 break;
169 }
170 if (s) sprintf (tmp,"%s: %.80s",s,host);
171 else sprintf (tmp,"Unknown resolver error (%ld): %.80s",
172 hst.rtnCode,host);
173 mm_log (tmp,ERROR);
174 return NIL;
175 }
176 }
177 }
178
179 /* create local TCP/IP stream */
180 stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
181 stream->ictr = 0; /* initialize input */
182 stream->pb.ioCRefNum = TCPdriver;
183 createpb = &stream->pb.csParam.create;
184 openpb = &stream->pb.csParam.open;
185 stream->pb.csCode = TCPCreate;/* create a TCP stream */
186 /* set up buffer for TCP */
187 createpb->rcvBuffLen = 4*BUFLEN;
188 createpb->rcvBuff = fs_get (createpb->rcvBuffLen);
189 createpb->notifyProc = NIL; /* no special notify procedure */
190 createpb->userDataPtr = NIL;
191 if (PBControlSync ((ParmBlkPtr) &stream->pb))
192 fatal ("Can't create TCP stream");
193 /* open TCP connection */
194 stream->pb.csCode = TCPActiveOpen;
195 openpb->ulpTimeoutValue = (int) ttmo_open;
196 openpb->ulpTimeoutAction = T;
197 openpb->validityFlags = timeoutValue|timeoutAction;
198 /* remote host (should try all) */
199 openpb->remoteHost = hst.addr[0];
200 openpb->remotePort = port; /* caller specified remote port */
201 openpb->localPort = 0; /* generate a local port */
202 openpb->tosFlags = 0; /* no special TOS */
203 openpb->precedence = 0; /* no special precedence */
204 openpb->dontFrag = 0; /* allow fragmentation */
205 openpb->timeToLive = 255; /* standards say 60, UNIX uses 255 */
206 openpb->security = 0; /* no special security */
207 openpb->optionCnt = 0; /* no IP options */
208 openpb->options[0] = 0;
209 openpb->userDataPtr = NIL; /* no special data pointer */
210 PBControlAsync ((ParmBlkPtr) &stream->pb);
211 while (stream->pb.ioResult == inProgress && wait ());
212 if (stream->pb.ioResult) { /* got back error status? */
213 sprintf (tmp,"Can't connect to %.80s,%ld",hst.cname,port);
214 mm_log (tmp,ERROR);
215 /* nuke the buffer */
216 stream->pb.csCode = TCPRelease;
217 createpb->userDataPtr = NIL;
218 if (PBControlSync ((ParmBlkPtr) &stream->pb)) fatal ("TCPRelease lossage");
219 /* free its buffer */
220 fs_give ((void **) &createpb->rcvBuff);
221 fs_give ((void **) &stream);/* and the local stream */
222 return NIL;
223 }
224
225 /* copy host names for later use */
226 stream->host = cpystr (hst.cname);
227 /* tie off trailing dot */
228 stream->host[strlen (stream->host) - 1] = '\0';
229 /* the open gave us our address */
230 i = (openpb->localHost >> 24) & 0xff;
231 j = (openpb->localHost >> 16) & 0xff;
232 k = (openpb->localHost >> 8) & 0xff;
233 l = openpb->localHost & 0xff;
234 sprintf (tmp,"[%ld.%ld.%ld.%ld]",i,j,k,l);
235 stream->localhost = cpystr (tmp);
236 if (!myLocalHost) myLocalHost = cpystr (tmp);
237 stream->port = port; /* copy port number */
238 return stream;
239 }
240
241
242 /* Called when have return from DNS
243 * Accepts: host info pointer
244 * user data pointer
245 */
246
247 ResultUPP tcp_dns_upp = NIL;
248
tcp_dns_result(struct hostInfo * hostInfoPtr,char * userDataPtr)249 pascal void tcp_dns_result (struct hostInfo *hostInfoPtr,char *userDataPtr)
250 {
251 /* dummy routine */
252 }
253
254 /* TCP/IP authenticated open
255 * Accepts: NETMBX specifier
256 * service name
257 * returned user name buffer
258 * Returns: TCP/IP stream if success else NIL
259 */
260
tcp_aopen(NETMBX * mb,char * service,char * usrbuf)261 TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
262 {
263 return NIL; /* no authenticated opens on Mac */
264 }
265
266 /* TCP receive line
267 * Accepts: TCP stream
268 * Returns: text line string or NIL if failure
269 */
270
tcp_getline(TCPSTREAM * stream)271 char *tcp_getline (TCPSTREAM *stream)
272 {
273 unsigned long n,contd;
274 char *ret = tcp_getline_work (stream,&n,&contd);
275 if (ret && contd) { /* got a line needing continuation? */
276 STRINGLIST *stl = mail_newstringlist ();
277 STRINGLIST *stc = stl;
278 do { /* collect additional lines */
279 stc->text.data = (unsigned char *) ret;
280 stc->text.size = n;
281 stc = stc->next = mail_newstringlist ();
282 ret = tcp_getline_work (stream,&n,&contd);
283 } while (ret && contd);
284 if (ret) { /* stash final part of line on list */
285 stc->text.data = (unsigned char *) ret;
286 stc->text.size = n;
287 /* determine how large a buffer we need */
288 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
289 ret = fs_get (n + 1); /* copy parts into buffer */
290 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
291 memcpy (ret + n,stc->text.data,stc->text.size);
292 ret[n] = '\0';
293 }
294 mail_free_stringlist (&stl);/* either way, done with list */
295 }
296 return ret;
297 }
298
299 /* TCP receive line or partial line
300 * Accepts: TCP stream
301 * pointer to return size
302 * pointer to return continuation flag
303 * Returns: text line string, size and continuation flag, or NIL if failure
304 */
305
tcp_getline_work(TCPSTREAM * stream,unsigned long * size,long * contd)306 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
307 long *contd)
308 {
309 unsigned long n;
310 char *s,*ret,c,d;
311 *contd = NIL; /* assume no continuation */
312 /* make sure have data */
313 if (!tcp_getdata (stream)) return NIL;
314 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
315 d = *stream->iptr++; /* slurp another character */
316 if ((c == '\015') && (d == '\012')) {
317 ret = (char *) fs_get (n--);
318 memcpy (ret,s,*size = n); /* copy into a free storage string */
319 ret[n] = '\0'; /* tie off string with null */
320 return ret;
321 }
322 }
323 /* copy partial string from buffer */
324 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
325 /* get more data from the net */
326 if (!tcp_getdata (stream)) fs_give ((void **) &ret);
327 /* special case of newline broken by buffer */
328 else if ((c == '\015') && (*stream->iptr == '\012')) {
329 stream->iptr++; /* eat the line feed */
330 stream->ictr--;
331 ret[*size = --n] = '\0'; /* tie off string with null */
332 }
333 else *contd = LONGT; /* continuation needed */
334 return ret;
335 }
336
337 /* TCP/IP receive buffer
338 * Accepts: TCP/IP stream
339 * size in bytes
340 * buffer to read into
341 * Returns: T if success, NIL otherwise
342 */
343
tcp_getbuffer(TCPSTREAM * stream,unsigned long size,char * buffer)344 long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
345 {
346 unsigned long n;
347 char *bufptr = buffer;
348 while (size > 0) { /* until request satisfied */
349 if (!tcp_getdata (stream)) return NIL;
350 n = min (size,stream->ictr);/* number of bytes to transfer */
351 /* do the copy */
352 memcpy (bufptr,stream->iptr,n);
353 bufptr += n; /* update pointer */
354 stream->iptr +=n;
355 size -= n; /* update # of bytes to do */
356 stream->ictr -=n;
357 }
358 bufptr[0] = '\0'; /* tie off string */
359 return T;
360 }
361
362
363 /* TCP/IP receive data
364 * Accepts: TCP/IP stream
365 * Returns: T if success, NIL otherwise
366 */
367
tcp_getdata(TCPSTREAM * stream)368 long tcp_getdata (TCPSTREAM *stream)
369 {
370 time_t t = time (0);
371 struct TCPReceivePB *receivepb = &stream->pb.csParam.receive;
372 struct TCPAbortPB *abortpb = &stream->pb.csParam.abort;
373 while (stream->ictr < 1) { /* if nothing in the buffer */
374 time_t tl = time (0);
375 stream->pb.csCode = TCPRcv; /* receive TCP data */
376 receivepb->commandTimeoutValue = (int) ttmo_read;
377 receivepb->rcvBuff = stream->ibuf;
378 receivepb->rcvBuffLen = BUFLEN;
379 receivepb->secondTimeStamp = 0;
380 receivepb->userDataPtr = NIL;
381 PBControlAsync ((ParmBlkPtr) &stream->pb);
382 while (stream->pb.ioResult == inProgress && wait ());
383 if (stream->pb.ioResult) { /* punt if got an error */
384 time_t tc = time (0);
385 if ((stream->pb.ioResult == commandTimeout) && tmoh &&
386 ((*tmoh) (tc - t,tc - tl))) continue;
387 /* nuke connection */
388 stream->pb.csCode = TCPAbort;
389 abortpb->userDataPtr = NIL;
390 PBControlSync ((ParmBlkPtr) &stream->pb);
391 return NIL;
392 }
393 stream->iptr = stream->ibuf;/* point at TCP buffer */
394 stream->ictr = receivepb->rcvBuffLen;
395 }
396 return T;
397 }
398
399 /* TCP/IP send string as record
400 * Accepts: TCP/IP stream
401 * string pointer
402 * Returns: T if success else NIL
403 */
404
tcp_soutr(TCPSTREAM * stream,char * string)405 long tcp_soutr (TCPSTREAM *stream,char *string)
406 {
407 return tcp_sout (stream,string,(unsigned long) strlen (string));
408 }
409
410
411 /* TCP/IP send string
412 * Accepts: TCP/IP stream
413 * string pointer
414 * byte count
415 * Returns: T if success else NIL
416 */
417
tcp_sout(TCPSTREAM * stream,char * string,unsigned long size)418 long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
419 {
420 struct TCPSendPB *sendpb = &stream->pb.csParam.send;
421 struct TCPAbortPB *abortpb = &stream->pb.csParam.abort;
422 struct {
423 unsigned short length;
424 Ptr buffer;
425 unsigned short trailer;
426 } wds;
427 while (wds.length = (size > (unsigned long) 32768) ? 32768 : size) {
428 wds.buffer = string; /* buffer */
429 wds.trailer = 0; /* tie off buffer */
430 size -= wds.length; /* this many words will be output */
431 string += wds.length;
432 stream->pb.csCode = TCPSend;/* send TCP data */
433 sendpb->ulpTimeoutValue = (int) ttmo_write;
434 sendpb->ulpTimeoutAction = 0;
435 sendpb->validityFlags = timeoutValue|timeoutAction;
436 sendpb->pushFlag = T; /* send the data now */
437 sendpb->urgentFlag = NIL; /* non-urgent data */
438 sendpb->wdsPtr = (Ptr) &wds;
439 sendpb->userDataPtr = NIL;
440 PBControlAsync ((ParmBlkPtr) &stream->pb);
441 while (stream->pb.ioResult == inProgress && wait ());
442 if (stream->pb.ioResult) { /* punt if got an error */
443 /* nuke connection */
444 stream->pb.csCode =TCPAbort;
445 abortpb->userDataPtr = NIL;
446 PBControlSync ((ParmBlkPtr) &stream->pb);
447 return NIL;
448 }
449 }
450 return T; /* success */
451 }
452
453 /* TCP/IP close
454 * Accepts: TCP/IP stream
455 */
456
tcp_close(TCPSTREAM * stream)457 void tcp_close (TCPSTREAM *stream)
458 {
459 struct TCPClosePB *closepb = &stream->pb.csParam.close;
460 struct TCPCreatePB *createpb = &stream->pb.csParam.create;
461 stream->pb.csCode = TCPClose; /* close TCP stream */
462 closepb->ulpTimeoutValue = (int) ttmo_close;
463 closepb->ulpTimeoutAction = 0;
464 closepb->validityFlags = timeoutValue|timeoutAction;
465 closepb->userDataPtr = NIL;
466 PBControlAsync ((ParmBlkPtr) &stream->pb);
467 while (stream->pb.ioResult == inProgress && wait ());
468 stream->pb.csCode =TCPRelease;/* flush the buffers */
469 createpb->userDataPtr = NIL;
470 if (PBControlSync ((ParmBlkPtr) &stream->pb)) fatal ("TCPRelease lossage");
471 /* free its buffer */
472 fs_give ((void **) &createpb->rcvBuff);
473 /* flush host names */
474 fs_give ((void **) &stream->host);
475 fs_give ((void **) &stream->localhost);
476 fs_give ((void **) &stream); /* flush the stream */
477 }
478
479 /* TCP/IP return host for this stream
480 * Accepts: TCP/IP stream
481 * Returns: host name for this stream
482 */
483
tcp_host(TCPSTREAM * stream)484 char *tcp_host (TCPSTREAM *stream)
485 {
486 return stream->host; /* return host name */
487 }
488
489
490 /* TCP/IP return remote host for this stream
491 * Accepts: TCP/IP stream
492 * Returns: host name for this stream
493 */
494
tcp_remotehost(TCPSTREAM * stream)495 char *tcp_remotehost (TCPSTREAM *stream)
496 {
497 return stream->host; /* return host name */
498 }
499
500
501 /* TCP/IP return port for this stream
502 * Accepts: TCP/IP stream
503 * Returns: port number for this stream
504 */
505
tcp_port(TCPSTREAM * stream)506 unsigned long tcp_port (TCPSTREAM *stream)
507 {
508 return stream->port; /* return port number */
509 }
510
511
512 /* TCP/IP return local host for this stream
513 * Accepts: TCP/IP stream
514 * Returns: local host name for this stream
515 */
516
tcp_localhost(TCPSTREAM * stream)517 char *tcp_localhost (TCPSTREAM *stream)
518 {
519 return stream->localhost; /* return local host name */
520 }
521
522 /* TCP/IP return canonical form of host name
523 * Accepts: host name
524 * Returns: canonical form of host name
525 */
526
tcp_canonical(char * name)527 char *tcp_canonical (char *name)
528 {
529 int i;
530 struct hostInfo hst;
531 /* look like domain literal? */
532 if (name[0] == '[' && name[i = (strlen (name))-1] == ']') return name;
533 if (StrToAddr (name,&hst,tcp_dns_upp,NIL)) {
534 while (hst.rtnCode == cacheFault && wait ());
535 /* kludge around MacTCP bug */
536 if (hst.rtnCode == outOfMemory) {
537 mm_log ("Re-initializing domain resolver",WARN);
538 CloseResolver (); /* bop it on the head and try again */
539 OpenResolver (NIL); /* note this will leak 12K */
540 StrToAddr (name,&hst,tcp_dns_upp,NIL);
541 while (hst.rtnCode == cacheFault && wait ());
542 }
543 /* still have error status? */
544 if (hst.rtnCode) return name;
545 }
546 return hst.cname; /* success */
547 }
548
549
550 /* TCP/IP get client host name (server calls only)
551 * Returns: client host name
552 */
553
tcp_clienthost()554 char *tcp_clienthost ()
555 {
556 return "UNKNOWN";
557 }
558