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