Download
jcs
/wallops
/tcp.c
(View History)
jcs tcp: Sync with upstream, add atexit destroyer | Latest amendment: 51 on 2024-08-30 |
1 | /* |
2 | * Copyright (c) 2020 joshua stein <jcs@jcs.org> |
3 | * Copyright (c) 1990-1992 by the University of Illinois Board of Trustees |
4 | * |
5 | * Permission to use, copy, modify, and distribute this software for any |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. |
8 | * |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | */ |
17 | |
18 | #include <stdio.h> |
19 | #include <stdlib.h> |
20 | #include <string.h> |
21 | #include "tcp.h" |
22 | |
23 | #define RCV_BUFFER_SIZE 1024 |
24 | #define TCP_BUFFER_SIZE 8192 |
25 | #define OPEN_TIMEOUT 60 |
26 | |
27 | short gIPPDriverRefNum; |
28 | |
29 | static short _TCPAtexitInstalled = 0; |
30 | static StreamPtr _TCPStreams[10] = { 0 }; |
31 | |
32 | void _TCPAtexit(void); |
33 | |
34 | OSErr |
35 | _TCPInit(void) |
36 | { |
37 | ParamBlockRec pb; |
38 | OSErr osErr; |
39 | |
40 | memset(&pb, 0, sizeof(pb)); |
41 | |
42 | gIPPDriverRefNum = -1; |
43 | |
44 | pb.ioParam.ioCompletion = 0; |
45 | pb.ioParam.ioNamePtr = "\p.IPP"; |
46 | pb.ioParam.ioPermssn = fsCurPerm; |
47 | |
48 | osErr = PBOpen(&pb, false); |
49 | if (noErr == osErr) |
50 | gIPPDriverRefNum = pb.ioParam.ioRefNum; |
51 | |
52 | if (!_TCPAtexitInstalled) { |
53 | _atexit(_TCPAtexit); |
54 | _TCPAtexitInstalled = 1; |
55 | } |
56 | |
57 | return osErr; |
58 | } |
59 | |
60 | void |
61 | _TCPAtexit(void) |
62 | { |
63 | short n; |
64 | TCPiopb pb = { 0 }; |
65 | |
66 | for (n = 0; n < (sizeof(_TCPStreams) / sizeof(_TCPStreams[0])); n++) { |
67 | if (_TCPStreams[n] != 0) { |
68 | _TCPAbort(&pb, _TCPStreams[n], nil, nil, false); |
69 | _TCPRelease(&pb, _TCPStreams[n], nil, nil, false); |
70 | _TCPStreams[n] = 0; |
71 | } |
72 | } |
73 | } |
74 | |
75 | OSErr |
76 | _TCPGetOurIP(ip_addr *ip, long *netMask) |
77 | { |
78 | OSErr osErr; |
79 | GetAddrParamBlock pb; |
80 | |
81 | memset(&pb, 0, sizeof(pb)); |
82 | |
83 | pb.csCode = ipctlGetAddr; |
84 | pb.ioCRefNum = gIPPDriverRefNum; |
85 | pb.ioResult = 1; |
86 | |
87 | osErr = PBControl((ParmBlkPtr)&pb, true); |
88 | while (pb.ioResult > 0) |
89 | ; |
90 | |
91 | if (pb.ioResult != noErr) |
92 | return pb.ioResult; |
93 | |
94 | if (ip != NULL) |
95 | *ip = pb.ourAddress; |
96 | if (netMask != NULL) |
97 | *netMask = pb.ourNetMask; |
98 | |
99 | return osErr; |
100 | } |
101 | |
102 | OSErr |
103 | _TCPCreate(TCPiopb *pb, StreamPtr *stream, Ptr rcvBufPtr, long rcvBufLen, |
104 | TCPNotifyProc aNotifyProc, Ptr userDataPtr, |
105 | TCPIOCompletionProc ioCompletion, Boolean async) |
106 | { |
107 | OSErr osErr; |
108 | short n; |
109 | |
110 | memset(pb, 0, sizeof(*pb)); |
111 | |
112 | pb->csCode = TCPCreate; |
113 | pb->ioCompletion = ioCompletion; |
114 | pb->ioCRefNum = gIPPDriverRefNum; |
115 | pb->ioResult = 1; |
116 | |
117 | pb->csParam.create.rcvBuff = rcvBufPtr; |
118 | pb->csParam.create.rcvBuffLen = rcvBufLen; |
119 | pb->csParam.create.notifyProc = aNotifyProc; |
120 | pb->csParam.create.userDataPtr = userDataPtr; |
121 | |
122 | osErr = PBControl((ParmBlkPtr)pb, async); |
123 | if (!async && (noErr == osErr)) { |
124 | *stream = pb->tcpStream; |
125 | |
126 | for (n = 0; n < (sizeof(_TCPStreams) / sizeof(_TCPStreams[0])); n++) { |
127 | if (_TCPStreams[n] == 0) { |
128 | _TCPStreams[n] = pb->tcpStream; |
129 | break; |
130 | } |
131 | } |
132 | } |
133 | |
134 | return osErr; |
135 | } |
136 | |
137 | /* listen for an incoming connection */ |
138 | OSErr |
139 | _TCPPassiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr *remoteIP, |
140 | tcp_port *remotePort, ip_addr *localIP, tcp_port *localPort, |
141 | Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async) |
142 | { |
143 | OSErr osErr; |
144 | |
145 | memset(pb, 0, sizeof(*pb)); |
146 | |
147 | pb->csCode = TCPPassiveOpen; |
148 | pb->ioCompletion = ioCompletion; |
149 | pb->ioCRefNum = gIPPDriverRefNum; |
150 | pb->tcpStream = stream; |
151 | pb->ioResult = 1; |
152 | |
153 | pb->csParam.open.ulpTimeoutAction = 1; /* abort half-open connection */ |
154 | pb->csParam.open.ulpTimeoutValue = 5; /* after 5 seconds */ |
155 | pb->csParam.open.validityFlags = 0xC0; |
156 | pb->csParam.open.commandTimeoutValue = 0; |
157 | pb->csParam.open.remoteHost = 0; |
158 | pb->csParam.open.remotePort = 0; |
159 | pb->csParam.open.localHost = 0; |
160 | pb->csParam.open.localPort = *localPort; |
161 | pb->csParam.open.tosFlags = 0x1; /* low delay */ |
162 | pb->csParam.open.precedence = 0; |
163 | pb->csParam.open.dontFrag = 0; |
164 | pb->csParam.open.timeToLive = 0; |
165 | pb->csParam.open.security = 0; |
166 | pb->csParam.open.optionCnt = 0; |
167 | pb->csParam.open.userDataPtr = userData; |
168 | |
169 | osErr = PBControl((ParmBlkPtr) pb, async); |
170 | if (!async && (osErr == noErr)) { |
171 | if (remoteIP) |
172 | *remoteIP = pb->csParam.open.remoteHost; |
173 | if (remotePort) |
174 | *remotePort = pb->csParam.open.remotePort; |
175 | if (localIP) |
176 | *localIP = pb->csParam.open.localHost; |
177 | *localPort = pb->csParam.open.localPort; |
178 | } |
179 | |
180 | return osErr; |
181 | } |
182 | |
183 | /* make an outgoing connection */ |
184 | OSErr |
185 | _TCPActiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr remoteIP, |
186 | tcp_port remotePort, ip_addr *localIP, tcp_port *localPort, |
187 | Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async) |
188 | { |
189 | OSErr osErr; |
190 | short index; |
191 | |
192 | memset(pb, 0, sizeof(*pb)); |
193 | |
194 | pb->csCode = TCPActiveOpen; |
195 | pb->ioCompletion = ioCompletion; |
196 | pb->ioCRefNum = gIPPDriverRefNum; |
197 | pb->tcpStream = stream; |
198 | pb->ioResult = 1; |
199 | |
200 | pb->csParam.open.ulpTimeoutValue = 30; |
201 | pb->csParam.open.ulpTimeoutAction = 1; |
202 | pb->csParam.open.validityFlags = 0xC0; |
203 | #if 0 |
204 | /* not available with this csCode */ |
205 | pb->csParam.open.commandTimeoutValue = 30; |
206 | #endif |
207 | pb->csParam.open.remoteHost = remoteIP; |
208 | pb->csParam.open.remotePort = remotePort; |
209 | pb->csParam.open.localHost = 0; |
210 | pb->csParam.open.localPort = *localPort; |
211 | pb->csParam.open.tosFlags = 0; |
212 | pb->csParam.open.precedence = 0; |
213 | pb->csParam.open.dontFrag = 0; |
214 | pb->csParam.open.timeToLive = 0; |
215 | pb->csParam.open.security = 0; |
216 | pb->csParam.open.optionCnt = 0; |
217 | for (index = 0; index < sizeof(pb->csParam.open.options); ++index) |
218 | pb->csParam.open.options[index] = 0; |
219 | pb->csParam.open.userDataPtr = userData; |
220 | |
221 | osErr = PBControl((ParmBlkPtr) pb, async); |
222 | if (!async && (osErr == noErr)) { |
223 | *localIP = pb->csParam.open.localHost; |
224 | *localPort = pb->csParam.open.localPort; |
225 | } |
226 | |
227 | return osErr; |
228 | } |
229 | |
230 | OSErr |
231 | _TCPSend(TCPiopb *pb, StreamPtr stream, wdsEntry *wdsPtr, Ptr userData, |
232 | TCPIOCompletionProc ioCompletion, Boolean async) |
233 | { |
234 | memset(pb, 0, sizeof(*pb)); |
235 | |
236 | pb->csCode = TCPSend; |
237 | pb->ioCompletion = ioCompletion; |
238 | pb->ioCRefNum = gIPPDriverRefNum; |
239 | pb->tcpStream = stream; |
240 | pb->ioResult = 1; |
241 | |
242 | pb->csParam.send.ulpTimeoutValue = 30; |
243 | pb->csParam.send.ulpTimeoutAction = 1; |
244 | pb->csParam.send.validityFlags = 0xC0; |
245 | pb->csParam.send.pushFlag = 1; /* XXX */ |
246 | pb->csParam.send.urgentFlag = 0; |
247 | pb->csParam.send.wdsPtr = (Ptr)wdsPtr; |
248 | pb->csParam.send.sendFree = 0; |
249 | pb->csParam.send.sendLength = 0; |
250 | pb->csParam.send.userDataPtr = userData; |
251 | |
252 | return PBControl((ParmBlkPtr)pb, async); |
253 | } |
254 | |
255 | OSErr |
256 | _TCPNoCopyRcv(TCPiopb *pb, StreamPtr stream, Ptr rdsPtr, |
257 | unsigned short rdsLength, Ptr userData, TCPIOCompletionProc ioCompletion, |
258 | Boolean async) |
259 | { |
260 | memset(pb, 0, sizeof(*pb)); |
261 | |
262 | pb->csCode = TCPNoCopyRcv; |
263 | pb->ioCompletion = ioCompletion; |
264 | pb->ioCRefNum = gIPPDriverRefNum; |
265 | pb->tcpStream = stream; |
266 | pb->ioResult = 1; |
267 | |
268 | pb->csParam.receive.commandTimeoutValue = 30; |
269 | pb->csParam.receive.urgentFlag = 0; |
270 | pb->csParam.receive.markFlag = 0; |
271 | pb->csParam.receive.rdsPtr = rdsPtr; |
272 | pb->csParam.receive.rdsLength = rdsLength; |
273 | pb->csParam.receive.userDataPtr = userData; |
274 | |
275 | return PBControl((ParmBlkPtr)pb, async); |
276 | } |
277 | |
278 | OSErr |
279 | _TCPRcv(TCPiopb *pb, StreamPtr stream, Ptr rcvBufPtr, |
280 | unsigned short *rcvBufLen, Ptr userData, TCPIOCompletionProc ioCompletion, |
281 | Boolean async) |
282 | { |
283 | OSErr osErr; |
284 | |
285 | memset(pb, 0, sizeof(*pb)); |
286 | |
287 | pb->csCode = TCPRcv; |
288 | pb->ioCompletion = ioCompletion; |
289 | pb->ioCRefNum = gIPPDriverRefNum; |
290 | pb->tcpStream = stream; |
291 | pb->ioResult = 1; |
292 | |
293 | pb->csParam.receive.commandTimeoutValue = 30; |
294 | pb->csParam.receive.urgentFlag = 0; |
295 | pb->csParam.receive.markFlag = 0; |
296 | pb->csParam.receive.rcvBuff = rcvBufPtr; |
297 | pb->csParam.receive.rcvBuffLen = *rcvBufLen; |
298 | pb->csParam.receive.userDataPtr = userData; |
299 | |
300 | osErr = PBControl((ParmBlkPtr)pb, async); |
301 | if (!async) |
302 | *rcvBufLen = pb->csParam.receive.rcvBuffLen; |
303 | |
304 | return osErr; |
305 | } |
306 | |
307 | OSErr |
308 | _TCPBfrReturn(TCPiopb *pb, StreamPtr stream, Ptr rdsPtr, Ptr userData, |
309 | TCPIOCompletionProc ioCompletion, Boolean async) |
310 | { |
311 | memset(pb, 0, sizeof(*pb)); |
312 | |
313 | pb->csCode = TCPRcvBfrReturn; |
314 | pb->ioCompletion = ioCompletion; |
315 | pb->ioCRefNum = gIPPDriverRefNum; |
316 | pb->tcpStream = stream; |
317 | pb->ioResult = 1; |
318 | |
319 | pb->csParam.receive.rdsPtr = rdsPtr; |
320 | pb->csParam.receive.userDataPtr = userData; |
321 | |
322 | return PBControl((ParmBlkPtr)pb, async); |
323 | } |
324 | |
325 | OSErr |
326 | _TCPClose(TCPiopb *pb, StreamPtr stream, Ptr userData, |
327 | TCPIOCompletionProc ioCompletion, Boolean async) |
328 | { |
329 | memset(pb, 0, sizeof(*pb)); |
330 | |
331 | pb->csCode = TCPClose; |
332 | pb->ioCompletion = ioCompletion; |
333 | pb->ioCRefNum = gIPPDriverRefNum; |
334 | pb->tcpStream = stream; |
335 | pb->ioResult = 1; |
336 | |
337 | pb->csParam.close.ulpTimeoutValue = 30; |
338 | pb->csParam.close.ulpTimeoutAction = 1; |
339 | pb->csParam.close.validityFlags = 0xC0; |
340 | pb->csParam.close.userDataPtr = userData; |
341 | |
342 | return PBControl((ParmBlkPtr)pb, async); |
343 | } |
344 | |
345 | OSErr |
346 | _TCPAbort(TCPiopb *pb, StreamPtr stream, Ptr userData, |
347 | TCPIOCompletionProc ioCompletion, Boolean async) |
348 | { |
349 | memset(pb, 0, sizeof(*pb)); |
350 | |
351 | pb->csCode = TCPAbort; |
352 | pb->ioCompletion = ioCompletion; |
353 | pb->ioCRefNum = gIPPDriverRefNum; |
354 | pb->tcpStream = stream; |
355 | pb->ioResult = 1; |
356 | |
357 | pb->csParam.abort.userDataPtr = userData; |
358 | |
359 | return PBControl((ParmBlkPtr)pb, async); |
360 | } |
361 | |
362 | OSErr |
363 | _TCPStatus(TCPiopb *pb, StreamPtr stream, struct TCPStatusPB *status, |
364 | Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async) |
365 | { |
366 | OSErr osErr; |
367 | |
368 | pb->ioCompletion = ioCompletion; |
369 | pb->ioResult = 1; |
370 | pb->ioNamePtr = 0; |
371 | pb->ioVRefNum = 0; |
372 | pb->ioCRefNum = gIPPDriverRefNum; |
373 | pb->csCode = TCPStatus; |
374 | pb->tcpStream = stream; |
375 | pb->csParam.status.userDataPtr = userData; |
376 | |
377 | osErr = PBControl((ParmBlkPtr)pb, async); |
378 | if (!async && (noErr == osErr)) { |
379 | *status = pb->csParam.status; |
380 | } |
381 | |
382 | return osErr; |
383 | } |
384 | |
385 | OSErr |
386 | _TCPRelease(TCPiopb *pb, StreamPtr stream, Ptr userData, |
387 | TCPIOCompletionProc ioCompletion, Boolean async) |
388 | { |
389 | OSErr osErr; |
390 | short n; |
391 | |
392 | memset(pb, 0, sizeof(*pb)); |
393 | |
394 | pb->csCode = TCPRelease; |
395 | pb->ioCompletion = ioCompletion; |
396 | pb->ioCRefNum = gIPPDriverRefNum; |
397 | pb->tcpStream = stream; |
398 | pb->ioResult = 1; |
399 | |
400 | pb->csParam.status.userDataPtr = userData; |
401 | |
402 | osErr = PBControl((ParmBlkPtr)pb, async); |
403 | |
404 | for (n = 0; n < (sizeof(_TCPStreams) / sizeof(_TCPStreams[0])); n++) { |
405 | if (_TCPStreams[n] == stream) { |
406 | _TCPStreams[n] = 0; |
407 | break; |
408 | } |
409 | } |
410 | |
411 | return osErr; |
412 | } |
413 | |
414 | OSErr |
415 | _UDPMaxMTUSize(UDPiopb *pb, short *mtu) |
416 | { |
417 | OSErr osErr; |
418 | |
419 | memset(pb, 0, sizeof(*pb)); |
420 | |
421 | pb->csCode = UDPMaxMTUSize; |
422 | pb->ioCRefNum = gIPPDriverRefNum; |
423 | pb->ioResult = 1; |
424 | |
425 | pb->csParam.mtu.remoteHost = (ip_addr)0; |
426 | |
427 | osErr = PBControl((ParmBlkPtr)pb, false); |
428 | |
429 | if (osErr == noErr) |
430 | *mtu = pb->csParam.mtu.mtuSize; |
431 | |
432 | return osErr; |
433 | } |
434 | |
435 | OSErr |
436 | _UDPCreate(UDPiopb *pb, StreamPtr *stream, Ptr rcvBufPtr, long rcvBufLen, |
437 | UDPNotifyProc aNotifyProc, Ptr userDataPtr, |
438 | UDPIOCompletionProc ioCompletion, Boolean async) |
439 | { |
440 | OSErr osErr; |
441 | |
442 | memset(pb, 0, sizeof(*pb)); |
443 | |
444 | pb->csCode = UDPCreate; |
445 | pb->ioCompletion = ioCompletion; |
446 | pb->ioCRefNum = gIPPDriverRefNum; |
447 | pb->ioResult = 1; |
448 | |
449 | pb->csParam.create.rcvBuff = rcvBufPtr; |
450 | pb->csParam.create.rcvBuffLen = rcvBufLen; |
451 | pb->csParam.create.notifyProc = aNotifyProc; |
452 | pb->csParam.create.userDataPtr = userDataPtr; |
453 | |
454 | osErr = PBControl((ParmBlkPtr)pb, async); |
455 | if (!async && (noErr == osErr)) { |
456 | *stream = pb->udpStream; |
457 | } |
458 | |
459 | return osErr; |
460 | } |
461 | |
462 | OSErr |
463 | _UDPSend(UDPiopb *pb, StreamPtr stream, wdsEntry *wdsPtr, ip_addr remoteIP, |
464 | udp_port remotePort, Ptr userData, UDPIOCompletionProc ioCompletion, |
465 | Boolean async) |
466 | { |
467 | memset(pb, 0, sizeof(*pb)); |
468 | |
469 | pb->csCode = UDPWrite; |
470 | pb->ioCompletion = ioCompletion; |
471 | pb->ioCRefNum = gIPPDriverRefNum; |
472 | pb->udpStream = stream; |
473 | pb->ioResult = 1; |
474 | |
475 | pb->csParam.send.remoteHost = remoteIP; |
476 | pb->csParam.send.remotePort = remotePort; |
477 | pb->csParam.send.wdsPtr = (Ptr)wdsPtr; |
478 | pb->csParam.send.checkSum = 0; |
479 | pb->csParam.send.sendLength = 0; |
480 | pb->csParam.send.userDataPtr = userData; |
481 | |
482 | return PBControl((ParmBlkPtr)pb, async); |
483 | } |
484 | |
485 | OSErr |
486 | _UDPRelease(UDPiopb *pb, StreamPtr stream, Ptr userData, |
487 | UDPIOCompletionProc ioCompletion, Boolean async) |
488 | { |
489 | OSErr osErr; |
490 | |
491 | memset(pb, 0, sizeof(*pb)); |
492 | |
493 | pb->csCode = UDPRelease; |
494 | pb->ioCompletion = ioCompletion; |
495 | pb->ioCRefNum = gIPPDriverRefNum; |
496 | pb->udpStream = stream; |
497 | pb->ioResult = 1; |
498 | |
499 | //pb->csParam.status.userDataPtr = userData; |
500 | |
501 | osErr = PBControl((ParmBlkPtr)pb, async); |
502 | |
503 | return osErr; |
504 | } |
505 | |
506 | |
507 | /* convenience functions */ |
508 | |
509 | pascal void |
510 | StrToAddrMarkDone(struct hostInfo *hi, char *data) |
511 | { |
512 | volatile int *done = (int *)data; |
513 | *done = 1; |
514 | } |
515 | |
516 | unsigned long |
517 | ip2long(char *ip) |
518 | { |
519 | unsigned long address = 0; |
520 | short dotcount = 0, i; |
521 | unsigned short b = 0; |
522 | |
523 | for (i = 0; ip[i] != 0; i++) { |
524 | if (ip[i] == '.') { |
525 | if (++dotcount > 3) |
526 | return (0); |
527 | address <<= 8; |
528 | address |= b; |
529 | b = 0; |
530 | } else if (ip[i] >= '0' && ip[i] <= '9') { |
531 | b *= 10; |
532 | b += (ip[i] - '0'); |
533 | if (b > 255) |
534 | return (0); |
535 | } else |
536 | return (0); |
537 | } |
538 | |
539 | if (dotcount != 3) |
540 | return (0); |
541 | address <<= 8; |
542 | address |= b; |
543 | return address; |
544 | } |
545 | |
546 | void |
547 | long2ip(unsigned long num, char *ip) |
548 | { |
549 | unsigned char *tmp = (unsigned char *)# |
550 | sprintf(ip, "%d.%d.%d.%d", tmp[0], tmp[1], tmp[2], tmp[3]); |
551 | } |
552 | |
553 | #define SOCKS_VERSION_SOCKS5 0x5 |
554 | #define SOCKS_METHOD_AUTH_NONE 0x0 |
555 | #define SOCKS_REQUEST_CONNECT 0x1 |
556 | #define SOCKS_REQUEST_ATYP_DOMAINNAME 0x3 |
557 | #define SOCKS_REPLY_SUCCESS 0x0 |
558 | |
559 | OSErr |
560 | SOCKS5TCPActiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr socks_ip, |
561 | tcp_port socks_port, char *remote_host, tcp_port remote_port, |
562 | ip_addr *local_ip, tcp_port *local_port, Ptr user_data, |
563 | TCPIOCompletionProc io_completion, Boolean async) |
564 | { |
565 | OSErr err; |
566 | TCPStatusPB status_pb; |
567 | wdsEntry wds[2]; |
568 | char data[255] = { 0 }; |
569 | unsigned short len, remote_host_len; |
570 | |
571 | remote_host_len = strlen(remote_host); |
572 | if (remote_host_len + 7 > sizeof(data)) |
573 | return -1; |
574 | |
575 | err = _TCPActiveOpen(pb, stream, socks_ip, socks_port, local_ip, |
576 | local_port, user_data, io_completion, async); |
577 | if (err != noErr) |
578 | return err; |
579 | |
580 | data[0] = SOCKS_VERSION_SOCKS5; |
581 | data[1] = 1; /* nmethods */ |
582 | data[2] = SOCKS_METHOD_AUTH_NONE; |
583 | |
584 | memset(&wds, 0, sizeof(wds)); |
585 | wds[0].ptr = (Ptr)&data; |
586 | wds[0].length = 3; |
587 | |
588 | err = _TCPSend(pb, stream, wds, nil, nil, false); |
589 | if (err) |
590 | goto fail; |
591 | |
592 | for (;;) { |
593 | err = _TCPStatus(pb, stream, &status_pb, nil, nil, false); |
594 | if (err != noErr) |
595 | goto fail; |
596 | |
597 | if (status_pb.amtUnreadData >= 2) |
598 | break; |
599 | } |
600 | |
601 | len = 2; |
602 | err = _TCPRcv(pb, stream, (Ptr)&data, &len, nil, nil, false); |
603 | if (err != noErr) |
604 | goto fail; |
605 | |
606 | if (data[0] != SOCKS_VERSION_SOCKS5 || data[1] != SOCKS_METHOD_AUTH_NONE) |
607 | goto fail; |
608 | |
609 | len = 0; |
610 | data[len++] = SOCKS_VERSION_SOCKS5; |
611 | data[len++] = SOCKS_REQUEST_CONNECT; |
612 | data[len++] = 0; /* reserved */ |
613 | data[len++] = SOCKS_REQUEST_ATYP_DOMAINNAME; |
614 | data[len++] = remote_host_len; |
615 | memcpy(data + len, remote_host, remote_host_len); |
616 | len += remote_host_len; |
617 | data[len++] = (remote_port >> 8); |
618 | data[len++] = (remote_port & 0xff); |
619 | |
620 | memset(&wds, 0, sizeof(wds)); |
621 | wds[0].ptr = (Ptr)&data; |
622 | wds[0].length = len; |
623 | |
624 | err = _TCPSend(pb, stream, wds, nil, nil, false); |
625 | if (err) |
626 | goto fail; |
627 | |
628 | for (;;) { |
629 | err = _TCPStatus(pb, stream, &status_pb, nil, nil, false); |
630 | if (err != noErr) |
631 | goto fail; |
632 | |
633 | if (status_pb.amtUnreadData >= 7) |
634 | break; |
635 | } |
636 | |
637 | len = status_pb.amtUnreadData; |
638 | if (len > sizeof(data)) |
639 | len = sizeof(data); |
640 | err = _TCPRcv(pb, stream, (Ptr)&data, &len, nil, nil, false); |
641 | if (err != noErr) |
642 | goto fail; |
643 | |
644 | if (data[0] != SOCKS_VERSION_SOCKS5 || data[1] != SOCKS_REPLY_SUCCESS) |
645 | goto fail; |
646 | |
647 | return noErr; |
648 | |
649 | fail: |
650 | _TCPClose(pb, stream, nil, nil, false); |
651 | return err; |
652 | } |