delete .cvsignore
[reactos.git] / rosapps / net / ncftp / libncftp / ftp.c
1 /* ftp.c
2 *
3 * Copyright (c) 1996-2001 Mike Gleason, NCEMRSoft.
4 * All rights reserved.
5 *
6 */
7
8 #define _libncftp_ftp_c_
9 #include "syshdrs.h"
10
11 char gLibNcFTPVersion[64] = kLibraryVersion;
12
13 #ifdef NO_SIGNALS
14 static char gNoSignalsMarker[] = "@(#) LibNcFTP - NO_SIGNALS";
15 #else
16
17 static int gGotSig = 0;
18 #ifdef HAVE_SIGSETJMP
19 static sigjmp_buf gCancelConnectJmp;
20 #else
21 static jmp_buf gCancelConnectJmp;
22 #endif /* HAVE_SIGSETJMP */
23
24 #endif /* NO_SIGNALS */
25
26
27 #ifndef lint
28 static char gCopyright[] = "@(#) LibNcFTP Copyright 1995-2000, by Mike Gleason. All rights reserved.";
29 #endif
30
31 #ifdef HAVE_LIBSOCKS5
32 # define SOCKS 5
33 # include <socks.h>
34 #else
35 # ifdef HAVE_LIBSOCKS
36 # define accept Raccept
37 # define connect Rconnect
38 # define getsockname Rgetsockname
39 # define listen Rlisten
40 # endif
41 #endif
42
43
44
45
46 /* On entry, you should have 'host' be set to a symbolic name (like
47 * cse.unl.edu), or set to a numeric address (like 129.93.3.1).
48 * If the function fails, it will return NULL, but if the host was
49 * a numeric style address, you'll have the ip_address to fall back on.
50 */
51
52 static struct hostent *
53 GetHostEntry(char *host, struct in_addr *ip_address)
54 {
55 struct in_addr ip;
56 struct hostent *hp;
57
58 /* See if the host was given in the dotted IP format, like "36.44.0.2."
59 * If it was, inet_addr will convert that to a 32-bit binary value;
60 * it not, inet_addr will return (-1L).
61 */
62 ip.s_addr = inet_addr(host);
63 if (ip.s_addr != INADDR_NONE) {
64 hp = NULL;
65 } else {
66 /* No IP address, so it must be a hostname, like ftp.wustl.edu. */
67 hp = gethostbyname(host);
68 if (hp != NULL)
69 (void) memcpy(&ip.s_addr, hp->h_addr_list[0], (size_t) hp->h_length);
70 }
71 if (ip_address != NULL)
72 *ip_address = ip;
73 return (hp);
74 } /* GetHostEntry */
75
76
77
78
79 /* Makes every effort to return a fully qualified domain name. */
80 int
81 GetOurHostName(char *host, size_t siz)
82 {
83 #ifdef HOSTNAME
84 /* You can hardcode in the name if this routine doesn't work
85 * the way you want it to.
86 */
87 Strncpy(host, HOSTNAME, siz);
88 return (1); /* Success */
89 #else
90 struct hostent *hp;
91 int result;
92 char **curAlias;
93 char domain[64];
94 char *cp;
95 int rc;
96
97 host[0] = '\0';
98 result = gethostname(host, (int) siz);
99 if ((result < 0) || (host[0] == '\0')) {
100 return (-1);
101 }
102
103 if (strchr(host, '.') != NULL) {
104 /* gethostname returned full name (like "cse.unl.edu"), instead
105 * of just the node name (like "cse").
106 */
107 return (2); /* Success */
108 }
109
110 hp = gethostbyname(host);
111 if (hp != NULL) {
112 /* Maybe the host entry has the full name. */
113 cp = strchr((char *) hp->h_name, '.');
114 if ((cp != NULL) && (cp[1] != '\0')) {
115 /* The 'name' field for the host entry had full name. */
116 (void) Strncpy(host, (char *) hp->h_name, siz);
117 return (3); /* Success */
118 }
119
120 /* Now try the list of aliases, to see if any of those look real. */
121 for (curAlias = hp->h_aliases; *curAlias != NULL; curAlias++) {
122 cp = strchr(*curAlias, '.');
123 if ((cp != NULL) && (cp[1] != '\0')) {
124 (void) Strncpy(host, *curAlias, siz);
125 return (4); /* Success */
126 }
127 }
128 }
129
130 /* Otherwise, we just have the node name. See if we can get the
131 * domain name ourselves.
132 */
133 #ifdef DOMAINNAME
134 (void) STRNCPY(domain, DOMAINNAME);
135 rc = 5;
136 #else
137 rc = -1;
138 domain[0] = '\0';
139 # if defined(HAVE_RES_INIT) && defined(HAVE__RES_DEFDNAME)
140 if (domain[0] == '\0') {
141 (void) res_init();
142 if ((_res.defdname != NULL) && (_res.defdname[0] != '\0')) {
143 (void) STRNCPY(domain, _res.defdname);
144 rc = 6;
145 }
146 }
147 # endif /* HAVE_RES_INIT && HAVE__RES_DEFDNAME */
148
149 if (domain[0] == '\0') {
150 FILE *fp;
151 char line[256];
152 char *tok;
153
154 fp = fopen("/etc/resolv.conf", "r");
155 if (fp != NULL) {
156 (void) memset(line, 0, sizeof(line));
157 while (fgets(line, sizeof(line) - 1, fp) != NULL) {
158 if (!isalpha((int) line[0]))
159 continue; /* Skip comment lines. */
160 tok = strtok(line, " \t\n\r");
161 if (tok == NULL)
162 continue; /* Impossible */
163 if (strcmp(tok, "domain") == 0) {
164 tok = strtok(NULL, " \t\n\r");
165 if (tok == NULL)
166 continue; /* syntax error */
167 (void) STRNCPY(domain, tok);
168 rc = 7;
169 break; /* Done. */
170 }
171 }
172 (void) fclose(fp);
173 }
174 }
175 #endif /* DOMAINNAME */
176
177 if (domain[0] != '\0') {
178 /* Supposedly, it's legal for a domain name with
179 * a period at the end.
180 */
181 cp = domain + strlen(domain) - 1;
182 if (*cp == '.')
183 *cp = '\0';
184 if (domain[0] != '.')
185 (void) Strncat(host, ".", siz);
186 (void) Strncat(host, domain, siz);
187 }
188 if (rc < 0)
189 host[0] = '\0';
190 return(rc); /* Success */
191 #endif /* !HOSTNAME */
192 } /* GetOurHostName */
193
194
195
196 void
197 CloseControlConnection(const FTPCIPtr cip)
198 {
199 /* This will close each file, if it was open. */
200 #ifdef NO_SIGNALS
201 SClose(cip->ctrlSocketR, 3);
202 cip->ctrlSocketR = kClosedFileDescriptor;
203 cip->ctrlSocketW = kClosedFileDescriptor;
204 DisposeSReadlineInfo(&cip->ctrlSrl);
205 #else /* NO_SIGNALS */
206 if (cip->ctrlTimeout > 0)
207 (void) alarm(cip->ctrlTimeout);
208 CloseFile(&cip->cin);
209 CloseFile(&cip->cout);
210 cip->ctrlSocketR = kClosedFileDescriptor;
211 cip->ctrlSocketW = kClosedFileDescriptor;
212 if (cip->ctrlTimeout > 0)
213 (void) alarm(0);
214 #endif /* NO_SIGNALS */
215 cip->connected = 0;
216 cip->loggedIn = 0;
217 } /* CloseControlConnection */
218
219
220
221 static int
222 GetSocketAddress(const FTPCIPtr cip, int sockfd, struct sockaddr_in *saddr)
223 {
224 int len = (int) sizeof (struct sockaddr_in);
225 int result = 0;
226
227 if (getsockname(sockfd, (struct sockaddr *)saddr, &len) < 0) {
228 Error(cip, kDoPerror, "Could not get socket name.\n");
229 cip->errNo = kErrGetSockName;
230 result = kErrGetSockName;
231 }
232 return (result);
233 } /* GetSocketAddress */
234
235
236
237
238 int
239 SetKeepAlive(const FTPCIPtr cip, int sockfd)
240 {
241 #ifndef SO_KEEPALIVE
242 cip->errNo = kErrSetKeepAlive;
243 return (kErrSetKeepAlive);
244 #else
245 int opt;
246
247 opt = 1;
248
249 if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, (int) sizeof(opt)) < 0) {
250 /* Error(cip, kDoPerror, "Could not set keep-alive mode.\n"); */
251 cip->errNo = kErrSetKeepAlive;
252 return (kErrSetKeepAlive);
253 }
254 return (kNoErr);
255 #endif /* SO_KEEPALIVE */
256 } /* SetKeepAlive */
257
258
259
260
261 int
262 SetLinger(const FTPCIPtr cip, int sockfd, int onoff)
263 {
264 #ifndef SO_LINGER
265 cip->errNo = kErrSetLinger;
266 return (kErrSetLinger);
267 #else
268 struct linger li;
269
270 if (onoff != 0) {
271 li.l_onoff = 1;
272 li.l_linger = 120; /* 2 minutes, but system ignores field. */
273 } else {
274 li.l_onoff = 0;
275 li.l_linger = 0;
276 }
277 /* Have the system make an effort to deliver any unsent data,
278 * even after we close the connection.
279 */
280 if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &li, (int) sizeof(li)) < 0) {
281 /* Error(cip, kDoPerror, "Could not set linger mode.\n"); */
282 cip->errNo = kErrSetLinger;
283 return (kErrSetLinger);
284 }
285 return (kNoErr);
286 #endif /* SO_LINGER */
287 } /* SetLinger */
288
289
290
291
292 #ifdef IP_TOS
293 int
294 SetTypeOfService(const FTPCIPtr cip, int sockfd, int tosType)
295 {
296 /* Specify to the router what type of connection this is, so it
297 * can prioritize packets.
298 */
299 if (setsockopt(sockfd, IPPROTO_IP, IP_TOS, (char *) &tosType, (int) sizeof(tosType)) < 0) {
300 /* Error(cip, kDoPerror, "Could not set type of service.\n"); */
301 cip->errNo = kErrSetTypeOfService;
302 return (kErrSetTypeOfService);
303 }
304 return (kNoErr);
305 } /* SetTypeOfService */
306 #endif /* IP_TOS */
307
308
309
310
311 #ifdef SO_OOBINLINE
312 int
313 SetInlineOutOfBandData(const FTPCIPtr cip, int sockfd)
314 {
315 int on = 1;
316
317 if (setsockopt(sockfd, SOL_SOCKET, SO_OOBINLINE, (char *) &on, (int) sizeof(on)) < 0) {
318 Error(cip, kDoPerror, "Could not set out of band inline mode.\n");
319 cip->errNo = kErrSetOutOfBandInline;
320 return (kErrSetOutOfBandInline);
321 }
322 return (kNoErr);
323 } /* SetInlineOutOfBandData */
324 #endif /* SO_OOBINLINE */
325
326
327
328
329 #ifndef NO_SIGNALS
330
331 static void
332 CancelConnect(int signum)
333 {
334 gGotSig = signum;
335 #ifdef HAVE_SIGSETJMP
336 siglongjmp(gCancelConnectJmp, 1);
337 #else
338 longjmp(gCancelConnectJmp, 1);
339 #endif /* HAVE_SIGSETJMP */
340 } /* CancelConnect */
341
342 #endif /* NO_SIGNALS */
343
344
345
346 int
347 OpenControlConnection(const FTPCIPtr cip, char *host, unsigned int port)
348 {
349 struct in_addr ip_address;
350 int err = 0;
351 int result;
352 int oerrno;
353 volatile int sockfd = -1;
354 volatile int sock2fd = -1;
355 ResponsePtr rp;
356 char **volatile curaddr;
357 struct hostent *hp;
358 char *volatile fhost;
359 unsigned int fport;
360 #ifndef NO_SIGNALS
361 volatile FTPSigProc osigint;
362 volatile FTPSigProc osigalrm;
363 volatile FTPCIPtr vcip;
364 int sj;
365 #endif /* NO_SIGNALS */
366 const char *firstLine, *secondLine, *srvr;
367
368 LIBNCFTP_USE_VAR(gLibNcFTPVersion);
369 LIBNCFTP_USE_VAR(gCopyright);
370 #ifdef NO_SIGNALS
371 LIBNCFTP_USE_VAR(gNoSignalsMarker);
372 #endif /* NO_SIGNALS */
373
374 if (cip->firewallType == kFirewallNotInUse) {
375 fhost = host;
376 fport = port;
377 } else {
378 fhost = cip->firewallHost;
379 fport = cip->firewallPort;
380 }
381 if (fport == 0)
382 fport = cip->lip->defaultPort;
383
384 /* Since we're the client, we just have to get a socket() and
385 * connect() it.
386 */
387 (void) ZERO(cip->servCtlAddr);
388 cip->cin = NULL;
389 cip->cout = NULL;
390
391 /* Make sure we use network byte-order. */
392 fport = (unsigned int) htons((unsigned short) fport);
393
394 cip->servCtlAddr.sin_port = (unsigned short) fport;
395
396 hp = GetHostEntry(fhost, &ip_address);
397
398 if (hp == NULL) {
399 /* Okay, no Host entry, but maybe we have a numeric address
400 * in ip_address we can try.
401 */
402 if (ip_address.s_addr == INADDR_NONE) {
403 Error(cip, kDontPerror, "%s: unknown host.\n", fhost);
404 cip->errNo = kErrHostUnknown;
405 return (kErrHostUnknown);
406 }
407 cip->servCtlAddr.sin_family = AF_INET;
408 cip->servCtlAddr.sin_addr.s_addr = ip_address.s_addr;
409 } else {
410 cip->servCtlAddr.sin_family = hp->h_addrtype;
411 /* We'll fill in the rest of the structure below. */
412 }
413
414 /* After obtaining a socket, try to connect it to a remote
415 * address. If we didn't get a host entry, we will only have
416 * one thing to try (ip_address); if we do have one, we can try
417 * every address in the list from the host entry.
418 */
419
420 if (hp == NULL) {
421 /* Since we're given a single raw address, and not a host entry,
422 * we can only try this one address and not any other addresses
423 * that could be present for a site with a host entry.
424 */
425
426 if ((sockfd = socket(cip->servCtlAddr.sin_family, SOCK_STREAM, 0)) < 0) {
427 Error(cip, kDoPerror, "Could not get a socket.\n");
428 cip->errNo = kErrNewStreamSocket;
429 return (kErrNewStreamSocket);
430 }
431
432 /* This doesn't do anything if you left these
433 * at their defaults (zero). Otherwise it
434 * tries to set the buffer size to the
435 * size specified.
436 */
437 (void) SetSockBufSize(sockfd, cip->ctrlSocketRBufSize, cip->ctrlSocketSBufSize);
438
439 #ifdef NO_SIGNALS
440 err = SConnect(sockfd, &cip->servCtlAddr, (int) cip->connTimeout);
441
442 if (err < 0) {
443 oerrno = errno;
444 (void) SClose(sockfd, 3);
445 errno = oerrno;
446 sockfd = -1;
447 }
448 #else /* NO_SIGNALS */
449 osigint = (volatile FTPSigProc) signal(SIGINT, CancelConnect);
450 if (cip->connTimeout > 0) {
451 osigalrm = (volatile FTPSigProc) signal(SIGALRM, CancelConnect);
452 (void) alarm(cip->connTimeout);
453 }
454
455 vcip = cip;
456
457 #ifdef HAVE_SIGSETJMP
458 sj = sigsetjmp(gCancelConnectJmp, 1);
459 #else
460 sj = setjmp(gCancelConnectJmp);
461 #endif /* HAVE_SIGSETJMP */
462
463 if (sj != 0) {
464 /* Interrupted by a signal. */
465 (void) closesocket(sockfd);
466 (void) signal(SIGINT, (FTPSigProc) osigint);
467 if (vcip->connTimeout > 0) {
468 (void) alarm(0);
469 (void) signal(SIGALRM, (FTPSigProc) osigalrm);
470 }
471 if (gGotSig == SIGINT) {
472 result = vcip->errNo = kErrConnectMiscErr;
473 Error(vcip, kDontPerror, "Connection attempt canceled.\n");
474 (void) kill(getpid(), SIGINT);
475 } else if (gGotSig == SIGALRM) {
476 result = vcip->errNo = kErrConnectRetryableErr;
477 Error(vcip, kDontPerror, "Connection attempt timed-out.\n");
478 (void) kill(getpid(), SIGALRM);
479 } else {
480 result = vcip->errNo = kErrConnectMiscErr;
481 Error(vcip, kDontPerror, "Connection attempt failed due to an unexpected signal (%d).\n", gGotSig);
482 }
483 return (result);
484 } else {
485 err = connect(sockfd, (struct sockaddr *) &cip->servCtlAddr,
486 (int) sizeof (cip->servCtlAddr));
487 if (cip->connTimeout > 0) {
488 (void) alarm(0);
489 (void) signal(SIGALRM, (FTPSigProc) osigalrm);
490 }
491 (void) signal(SIGINT, (FTPSigProc) osigint);
492 }
493
494 if (err < 0) {
495 oerrno = errno;
496 (void) closesocket(sockfd);
497 errno = oerrno;
498 sockfd = -1;
499 }
500 #endif /* NO_SIGNALS */
501 } else {
502 /* We can try each address in the list. We'll quit when we
503 * run out of addresses to try or get a successful connection.
504 */
505 for (curaddr = hp->h_addr_list; *curaddr != NULL; curaddr++) {
506 if ((sockfd = socket(cip->servCtlAddr.sin_family, SOCK_STREAM, 0)) < 0) {
507 Error(cip, kDoPerror, "Could not get a socket.\n");
508 cip->errNo = kErrNewStreamSocket;
509 return (kErrNewStreamSocket);
510 }
511 /* This could overwrite the address field in the structure,
512 * but this is okay because the structure has a junk field
513 * just for this purpose.
514 */
515 (void) memcpy(&cip->servCtlAddr.sin_addr, *curaddr, (size_t) hp->h_length);
516
517 /* This doesn't do anything if you left these
518 * at their defaults (zero). Otherwise it
519 * tries to set the buffer size to the
520 * size specified.
521 */
522 (void) SetSockBufSize(sockfd, cip->ctrlSocketRBufSize, cip->ctrlSocketSBufSize);
523
524 #ifdef NO_SIGNALS
525 err = SConnect(sockfd, &cip->servCtlAddr, (int) cip->connTimeout);
526
527 if (err == 0)
528 break;
529 oerrno = errno;
530 (void) SClose(sockfd, 3);
531 errno = oerrno;
532 sockfd = -1;
533 #else /* NO_SIGNALS */
534
535 osigint = (volatile FTPSigProc) signal(SIGINT, CancelConnect);
536 if (cip->connTimeout > 0) {
537 osigalrm = (volatile FTPSigProc) signal(SIGALRM, CancelConnect);
538 (void) alarm(cip->connTimeout);
539 }
540
541 vcip = cip;
542 #ifdef HAVE_SIGSETJMP
543 sj = sigsetjmp(gCancelConnectJmp, 1);
544 #else
545 sj = setjmp(gCancelConnectJmp);
546 #endif /* HAVE_SIGSETJMP */
547
548 if (sj != 0) {
549 /* Interrupted by a signal. */
550 (void) closesocket(sockfd);
551 (void) signal(SIGINT, (FTPSigProc) osigint);
552 if (vcip->connTimeout > 0) {
553 (void) alarm(0);
554 (void) signal(SIGALRM, (FTPSigProc) osigalrm);
555 }
556 if (gGotSig == SIGINT) {
557 result = vcip->errNo = kErrConnectMiscErr;
558 Error(vcip, kDontPerror, "Connection attempt canceled.\n");
559 (void) kill(getpid(), SIGINT);
560 } else if (gGotSig == SIGALRM) {
561 result = vcip->errNo = kErrConnectRetryableErr;
562 Error(vcip, kDontPerror, "Connection attempt timed-out.\n");
563 (void) kill(getpid(), SIGALRM);
564 } else {
565 result = vcip->errNo = kErrConnectMiscErr;
566 Error(vcip, kDontPerror, "Connection attempt failed due to an unexpected signal (%d).\n", gGotSig);
567 }
568 return (result);
569 } else {
570 err = connect(sockfd, (struct sockaddr *) &cip->servCtlAddr,
571 (int) sizeof (cip->servCtlAddr));
572 if (cip->connTimeout > 0) {
573 (void) alarm(0);
574 (void) signal(SIGALRM, (FTPSigProc) osigalrm);
575 }
576 (void) signal(SIGINT, (FTPSigProc) osigint);
577 }
578
579 if (err == 0)
580 break;
581 oerrno = errno;
582 (void) closesocket(sockfd);
583 errno = oerrno;
584 sockfd = -1;
585 #endif /* NO_SIGNALS */
586 }
587 }
588
589 if (err < 0) {
590 /* Could not connect. Close up shop and go home. */
591
592 /* If possible, tell the caller if they should bother
593 * calling back later.
594 */
595 switch (errno) {
596 #ifdef ENETDOWN
597 case ENETDOWN:
598 #elif defined(WSAENETDOWN)
599 case WSAENETDOWN:
600 #endif
601 #ifdef ENETUNREACH
602 case ENETUNREACH:
603 #elif defined(WSAENETUNREACH)
604 case WSAENETUNREACH:
605 #endif
606 #ifdef ECONNABORTED
607 case ECONNABORTED:
608 #elif defined(WSAECONNABORTED)
609 case WSAECONNABORTED:
610 #endif
611 #ifdef ETIMEDOUT
612 case ETIMEDOUT:
613 #elif defined(WSAETIMEDOUT)
614 case WSAETIMEDOUT:
615 #endif
616 #ifdef EHOSTDOWN
617 case EHOSTDOWN:
618 #elif defined(WSAEHOSTDOWN)
619 case WSAEHOSTDOWN:
620 #endif
621 #ifdef ECONNRESET
622 case ECONNRESET:
623 #elif defined(WSAECONNRESET)
624 case WSAECONNRESET:
625 #endif
626 Error(cip, kDoPerror, "Could not connect to %s -- try again later.\n", fhost);
627 result = cip->errNo = kErrConnectRetryableErr;
628 break;
629 #ifdef ECONNREFUSED
630 case ECONNREFUSED:
631 #elif defined(WSAECONNREFUSED)
632 case WSAECONNREFUSED:
633 #endif
634 Error(cip, kDoPerror, "Could not connect to %s.\n", fhost);
635 result = cip->errNo = kErrConnectRefused;
636 break;
637 default:
638 Error(cip, kDoPerror, "Could not connect to %s.\n", fhost);
639 result = cip->errNo = kErrConnectMiscErr;
640 }
641 goto fatal;
642 }
643
644 /* Get our end of the socket address for later use. */
645 if ((result = GetSocketAddress(cip, sockfd, &cip->ourCtlAddr)) < 0)
646 goto fatal;
647
648 #ifdef SO_OOBINLINE
649 /* We want Out-of-band data to appear in the regular stream,
650 * since we can handle TELNET.
651 */
652 (void) SetInlineOutOfBandData(cip, sockfd);
653 #endif
654 (void) SetKeepAlive(cip, sockfd);
655 (void) SetLinger(cip, sockfd, 0); /* Don't need it for ctrl. */
656
657 #if defined(IP_TOS) && defined(IPTOS_LOWDELAY)
658 /* Control connection is somewhat interactive, so quick response
659 * is desired.
660 */
661 (void) SetTypeOfService(cip, sockfd, IPTOS_LOWDELAY);
662 #endif
663
664 #ifdef NO_SIGNALS
665 cip->ctrlSocketR = sockfd;
666 cip->ctrlSocketW = sockfd;
667 cip->cout = NULL;
668 cip->cin = NULL;
669 sock2fd = kClosedFileDescriptor;
670
671 if (InitSReadlineInfo(&cip->ctrlSrl, sockfd, cip->srlBuf, sizeof(cip->srlBuf), (int) cip->ctrlTimeout, 1) < 0) {
672 result = kErrFdopenW;
673 cip->errNo = kErrFdopenW;
674 Error(cip, kDoPerror, "Could not fdopen.\n");
675 goto fatal;
676 }
677 #else /* NO_SIGNALS */
678 if ((sock2fd = dup(sockfd)) < 0) {
679 result = kErrDupSocket;
680 cip->errNo = kErrDupSocket;
681 Error(cip, kDoPerror, "Could not duplicate a file descriptor.\n");
682 goto fatal;
683 }
684
685 /* Now setup the FILE pointers for use with the Std I/O library
686 * routines.
687 */
688 if ((cip->cin = fdopen(sockfd, "r")) == NULL) {
689 result = kErrFdopenR;
690 cip->errNo = kErrFdopenR;
691 Error(cip, kDoPerror, "Could not fdopen.\n");
692 goto fatal;
693 }
694
695 if ((cip->cout = fdopen(sock2fd, "w")) == NULL) {
696 result = kErrFdopenW;
697 cip->errNo = kErrFdopenW;
698 Error(cip, kDoPerror, "Could not fdopen.\n");
699 CloseFile(&cip->cin);
700 sockfd = kClosedFileDescriptor;
701 goto fatal;
702 }
703
704 cip->ctrlSocketR = sockfd;
705 cip->ctrlSocketW = sockfd;
706
707 /* We'll be reading and writing lines, so use line buffering. This
708 * is necessary since the stdio library will use full buffering
709 * for all streams not associated with the tty.
710 */
711 #ifdef HAVE_SETLINEBUF
712 setlinebuf(cip->cin);
713 setlinebuf(cip->cout);
714 #else
715 (void) SETVBUF(cip->cin, NULL, _IOLBF, (size_t) BUFSIZ);
716 (void) SETVBUF(cip->cout, NULL, _IOLBF, (size_t) BUFSIZ);
717 #endif
718 #endif /* NO_SIGNALS */
719
720 #ifdef HAVE_INET_NTOP /* Mostly to workaround bug in IRIX 6.5's inet_ntoa */
721 (void) memset(cip->ip, 0, sizeof(cip->ip));
722 (void) inet_ntop(AF_INET, &cip->servCtlAddr.sin_addr, cip->ip, sizeof(cip->ip) - 1);
723 #else
724 (void) STRNCPY(cip->ip, inet_ntoa(cip->servCtlAddr.sin_addr));
725 #endif
726 if ((hp == NULL) || (hp->h_name == NULL))
727 (void) STRNCPY(cip->actualHost, fhost);
728 else
729 (void) STRNCPY(cip->actualHost, (char *) hp->h_name);
730
731 /* Read the startup message from the server. */
732 rp = InitResponse();
733 if (rp == NULL) {
734 Error(cip, kDontPerror, "Malloc failed.\n");
735 cip->errNo = kErrMallocFailed;
736 result = cip->errNo;
737 goto fatal;
738 }
739
740 result = GetResponse(cip, rp);
741 if ((result < 0) && (rp->msg.first == NULL)) {
742 goto fatal;
743 }
744 if (rp->msg.first != NULL) {
745 cip->serverType = kServerTypeUnknown;
746 srvr = NULL;
747 firstLine = rp->msg.first->line;
748 secondLine = NULL;
749 if (rp->msg.first->next != NULL)
750 secondLine = rp->msg.first->next->line;
751
752 if (strstr(firstLine, "Version wu-") != NULL) {
753 cip->serverType = kServerTypeWuFTPd;
754 srvr = "wu-ftpd";
755 } else if (strstr(firstLine, "NcFTPd") != NULL) {
756 cip->serverType = kServerTypeNcFTPd;
757 srvr = "NcFTPd Server";
758 } else if (STRNEQ("ProFTPD", firstLine, 7)) {
759 cip->serverType = kServerTypeProFTPD;
760 srvr = "ProFTPD";
761 } else if (strstr(firstLine, "Microsoft FTP Service") != NULL) {
762 cip->serverType = kServerTypeMicrosoftFTP;
763 srvr = "Microsoft FTP Service";
764 } else if (strstr(firstLine, "(NetWare ") != NULL) {
765 cip->serverType = kServerTypeNetWareFTP;
766 srvr = "NetWare FTP Service";
767 } else if (STRNEQ("WFTPD", firstLine, 5)) {
768 cip->serverType = kServerTypeWFTPD;
769 srvr = "WFTPD";
770 } else if (STRNEQ("Serv-U FTP", firstLine, 10)) {
771 cip->serverType = kServerTypeServ_U;
772 srvr = "Serv-U FTP-Server";
773 } else if (strstr(firstLine, "VFTPD") != NULL) {
774 cip->serverType = kServerTypeVFTPD;
775 srvr = "VFTPD";
776 } else if (STRNEQ("FTP-Max", firstLine, 7)) {
777 cip->serverType = kServerTypeFTP_Max;
778 srvr = "FTP-Max";
779 } else if (strstr(firstLine, "Roxen") != NULL) {
780 cip->serverType = kServerTypeRoxen;
781 srvr = "Roxen";
782 } else if (strstr(firstLine, "WS_FTP") != NULL) {
783 cip->serverType = kServerTypeWS_FTP;
784 srvr = "WS_FTP Server";
785 } else if ((secondLine != NULL) && (strstr(secondLine, "WarFTP") != NULL)) {
786 cip->serverType = kServerTypeWarFTPd;
787 srvr = "WarFTPd";
788 }
789
790 if (srvr != NULL)
791 PrintF(cip, "Remote server is running %s.\n", srvr);
792
793 /* Do the application's connect message callback, if present. */
794 if ((cip->onConnectMsgProc != 0) && (rp->codeType < 4))
795 (*cip->onConnectMsgProc)(cip, rp);
796 }
797
798 if (rp->codeType >= 4) {
799 /* They probably hung up on us right away. That's too bad,
800 * but we can tell the caller that they can call back later
801 * and try again.
802 */
803 DoneWithResponse(cip, rp);
804 result = kErrConnectRetryableErr;
805 Error(cip, kDontPerror, "Server hungup immediately after connect.\n");
806 cip->errNo = kErrConnectRetryableErr;
807 goto fatal;
808 }
809 if (result < 0) /* Some other error occurred during connect message */
810 goto fatal;
811 cip->connected = 1;
812 DoneWithResponse(cip, rp);
813 return (kNoErr);
814
815 fatal:
816 if (sockfd > 0)
817 (void) closesocket(sockfd);
818 if (sock2fd > 0)
819 (void) closesocket(sock2fd);
820 CloseFile(&cip->cin);
821 CloseFile(&cip->cout);
822 cip->ctrlSocketR = kClosedFileDescriptor;
823 cip->ctrlSocketW = kClosedFileDescriptor;
824 return (result);
825 } /* OpenControlConnection */
826
827
828
829
830 void
831 CloseDataConnection(const FTPCIPtr cip)
832 {
833 if (cip->dataSocket != kClosedFileDescriptor) {
834 #ifdef NO_SIGNALS
835 SClose(cip->dataSocket, 3);
836 #else /* NO_SIGNALS */
837 if (cip->xferTimeout > 0)
838 (void) alarm(cip->xferTimeout);
839 (void) closesocket(cip->dataSocket);
840 if (cip->xferTimeout > 0)
841 (void) alarm(0);
842 #endif /* NO_SIGNALS */
843 cip->dataSocket = kClosedFileDescriptor;
844 }
845 memset(&cip->ourDataAddr, 0, sizeof(cip->ourDataAddr));
846 memset(&cip->servDataAddr, 0, sizeof(cip->servDataAddr));
847 } /* CloseDataConnection */
848
849
850
851
852 int
853 SetStartOffset(const FTPCIPtr cip, longest_int restartPt)
854 {
855 ResponsePtr rp;
856 int result;
857
858 if (restartPt != (longest_int) 0) {
859 rp = InitResponse();
860 if (rp == NULL) {
861 Error(cip, kDontPerror, "Malloc failed.\n");
862 cip->errNo = kErrMallocFailed;
863 return (cip->errNo);
864 }
865
866 /* Force reset to offset zero. */
867 if (restartPt == (longest_int) -1)
868 restartPt = (longest_int) 0;
869 #ifdef PRINTF_LONG_LONG
870 result = RCmd(cip, rp,
871 "REST " PRINTF_LONG_LONG,
872 restartPt);
873 #else
874 result = RCmd(cip, rp, "REST %ld", (long) restartPt);
875 #endif
876
877 if (result < 0) {
878 return (result);
879 } else if (result == 3) {
880 cip->hasREST = kCommandAvailable;
881 DoneWithResponse(cip, rp);
882 } else if (UNIMPLEMENTED_CMD(rp->code)) {
883 cip->hasREST = kCommandNotAvailable;
884 DoneWithResponse(cip, rp);
885 cip->errNo = kErrSetStartPoint;
886 return (kErrSetStartPoint);
887 } else {
888 DoneWithResponse(cip, rp);
889 cip->errNo = kErrSetStartPoint;
890 return (kErrSetStartPoint);
891 }
892 }
893 return (0);
894 } /* SetStartOffset */
895
896
897
898 static int
899 SendPort(const FTPCIPtr cip, struct sockaddr_in *saddr)
900 {
901 char *a, *p;
902 int result;
903 ResponsePtr rp;
904
905 rp = InitResponse();
906 if (rp == NULL) {
907 Error(cip, kDontPerror, "Malloc failed.\n");
908 cip->errNo = kErrMallocFailed;
909 return (cip->errNo);
910 }
911
912 /* These will point to data in network byte order. */
913 a = (char *) &saddr->sin_addr;
914 p = (char *) &saddr->sin_port;
915 #define UC(x) (int) (((int) x) & 0xff)
916
917 /* Need to tell the other side which host (the address) and
918 * which process (port) on that host to send data to.
919 */
920 result = RCmd(cip, rp, "PORT %d,%d,%d,%d,%d,%d",
921 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
922
923 if (result < 0) {
924 return (result);
925 } else if (result != 2) {
926 /* A 500'ish response code means the PORT command failed. */
927 DoneWithResponse(cip, rp);
928 cip->errNo = kErrPORTFailed;
929 return (cip->errNo);
930 }
931 DoneWithResponse(cip, rp);
932 return (kNoErr);
933 } /* SendPort */
934
935
936
937
938 static int
939 Passive(const FTPCIPtr cip, struct sockaddr_in *saddr, int *weird)
940 {
941 ResponsePtr rp;
942 int i[6], j;
943 unsigned char n[6];
944 char *cp;
945 int result;
946
947 rp = InitResponse();
948 if (rp == NULL) {
949 Error(cip, kDontPerror, "Malloc failed.\n");
950 cip->errNo = kErrMallocFailed;
951 return (cip->errNo);
952 }
953
954 result = RCmd(cip, rp, "PASV");
955 if (result < 0)
956 goto done;
957
958 if (rp->codeType != 2) {
959 /* Didn't understand or didn't want passive port selection. */
960 cip->errNo = result = kErrPASVFailed;
961 goto done;
962 }
963
964 /* The other side returns a specification in the form of
965 * an internet address as the first four integers (each
966 * integer stands for 8-bits of the real 32-bit address),
967 * and two more integers for the port (16-bit port).
968 *
969 * It should give us something like:
970 * "Entering Passive Mode (129,93,33,1,10,187)", so look for
971 * digits with sscanf() starting 24 characters down the string.
972 */
973 for (cp = rp->msg.first->line; ; cp++) {
974 if (*cp == '\0') {
975 Error(cip, kDontPerror, "Cannot parse PASV response: %s\n", rp->msg.first->line);
976 goto done;
977 }
978 if (isdigit((int) *cp))
979 break;
980 }
981
982 if (sscanf(cp, "%d,%d,%d,%d,%d,%d",
983 &i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6) {
984 Error(cip, kDontPerror, "Cannot parse PASV response: %s\n", rp->msg.first->line);
985 goto done;
986 }
987
988 for (j=0, *weird = 0; j<6; j++) {
989 /* Some ftp servers return bogus port octets, such as
990 * boombox.micro.umn.edu. Let the caller know if we got a
991 * weird looking octet.
992 */
993 if ((i[j] < 0) || (i[j] > 255))
994 *weird = *weird + 1;
995 n[j] = (unsigned char) (i[j] & 0xff);
996 }
997
998 (void) memcpy(&saddr->sin_addr, &n[0], (size_t) 4);
999 (void) memcpy(&saddr->sin_port, &n[4], (size_t) 2);
1000
1001 result = kNoErr;
1002 done:
1003 DoneWithResponse(cip, rp);
1004 return (result);
1005 } /* Passive */
1006
1007
1008
1009
1010 static int
1011 BindToEphemeralPortNumber(int sockfd, struct sockaddr_in *addrp, int ephemLo, int ephemHi)
1012 {
1013 int i;
1014 int result;
1015 int rangesize;
1016 unsigned short port;
1017
1018 addrp->sin_family = AF_INET;
1019 if (((int) ephemLo == 0) || ((int) ephemLo >= (int) ephemHi)) {
1020 /* Do it the normal way. System will
1021 * pick one, typically in the range
1022 * of 1024-4999.
1023 */
1024 addrp->sin_port = 0; /* Let system pick one. */
1025
1026 result = bind(sockfd, (struct sockaddr *) addrp, sizeof(struct sockaddr_in));
1027 } else {
1028 rangesize = (int) ((int) ephemHi - (int) ephemLo);
1029 result = 0;
1030 for (i=0; i<10; i++) {
1031 port = (unsigned short) (((int) rand() % rangesize) + (int) ephemLo);
1032 addrp->sin_port = port;
1033
1034 result = bind(sockfd, (struct sockaddr *) addrp, sizeof(struct sockaddr_in));
1035 if (result == 0)
1036 break;
1037 if ((errno != 999)
1038 /* This next line is just fodder to
1039 * shut the compiler up about variable
1040 * not being used.
1041 */
1042 && (gCopyright[0] != '\0'))
1043 break;
1044 }
1045 }
1046 return (result);
1047 } /* BindToEphemeralPortNumber */
1048
1049
1050
1051
1052 int
1053 OpenDataConnection(const FTPCIPtr cip, int mode)
1054 {
1055 int dataSocket;
1056 int weirdPort;
1057 int result;
1058
1059 /* Before we can transfer any data, and before we even ask the
1060 * remote server to start transferring via RETR/NLST/etc, we have
1061 * to setup the connection.
1062 */
1063
1064 tryPort2:
1065 weirdPort = 0;
1066 result = 0;
1067 CloseDataConnection(cip); /* In case we didn't before... */
1068
1069 dataSocket = socket(AF_INET, SOCK_STREAM, 0);
1070 if (dataSocket < 0) {
1071 Error(cip, kDoPerror, "Could not get a data socket.\n");
1072 result = kErrNewStreamSocket;
1073 cip->errNo = kErrNewStreamSocket;
1074 return result;
1075 }
1076
1077 /* This doesn't do anything if you left these
1078 * at their defaults (zero). Otherwise it
1079 * tries to set the buffer size to the
1080 * size specified.
1081 */
1082 (void) SetSockBufSize(dataSocket, cip->dataSocketRBufSize, cip->dataSocketSBufSize);
1083
1084 if ((cip->hasPASV == kCommandNotAvailable) || (mode == kSendPortMode)) {
1085 tryPort:
1086 cip->ourDataAddr = cip->ourCtlAddr;
1087 cip->ourDataAddr.sin_family = AF_INET;
1088
1089 #ifdef HAVE_LIBSOCKS
1090 cip->ourDataAddr.sin_port = 0;
1091 if (Rbind(dataSocket, (struct sockaddr *) &cip->ourDataAddr,
1092 (int) sizeof (cip->ourDataAddr),
1093 cip->servCtlAddr.sin_addr.s_addr) < 0)
1094 #else
1095 if (BindToEphemeralPortNumber(dataSocket, &cip->ourDataAddr, (int) cip->ephemLo, (int) cip->ephemHi) < 0)
1096 #endif
1097 {
1098 Error(cip, kDoPerror, "Could not bind the data socket");
1099 result = kErrBindDataSocket;
1100 cip->errNo = kErrBindDataSocket;
1101 goto bad;
1102 }
1103
1104 /* Need to do this so we can figure out which port the system
1105 * gave to us.
1106 */
1107 if ((result = GetSocketAddress(cip, dataSocket, &cip->ourDataAddr)) < 0)
1108 goto bad;
1109
1110 if (listen(dataSocket, 1) < 0) {
1111 Error(cip, kDoPerror, "listen failed");
1112 result = kErrListenDataSocket;
1113 cip->errNo = kErrListenDataSocket;
1114 goto bad;
1115 }
1116
1117 if ((result = SendPort(cip, &cip->ourDataAddr)) < 0)
1118 goto bad;
1119
1120 cip->dataPortMode = kSendPortMode;
1121 } else {
1122 /* Passive mode. Let the other side decide where to send. */
1123
1124 cip->servDataAddr = cip->servCtlAddr;
1125 cip->servDataAddr.sin_family = AF_INET;
1126 cip->ourDataAddr = cip->ourCtlAddr;
1127 cip->ourDataAddr.sin_family = AF_INET;
1128
1129 if (Passive(cip, &cip->servDataAddr, &weirdPort) < 0) {
1130 Error(cip, kDontPerror, "Passive mode refused.\n");
1131 cip->hasPASV = kCommandNotAvailable;
1132
1133 /* We can try using regular PORT commands, which are required
1134 * by all FTP protocol compliant programs, if you said so.
1135 *
1136 * We don't do this automatically, because if your host
1137 * is running a firewall you (probably) do not want SendPort
1138 * FTP for security reasons.
1139 */
1140 if (mode == kFallBackToSendPortMode)
1141 goto tryPort;
1142 result = kErrPassiveModeFailed;
1143 cip->errNo = kErrPassiveModeFailed;
1144 goto bad;
1145 }
1146
1147 #ifdef HAVE_LIBSOCKS
1148 cip->ourDataAddr.sin_port = 0;
1149 if (Rbind(dataSocket, (struct sockaddr *) &cip->ourDataAddr,
1150 (int) sizeof (cip->ourDataAddr),
1151 cip->servCtlAddr.sin_addr.s_addr) < 0)
1152 #else
1153 if (BindToEphemeralPortNumber(dataSocket, &cip->ourDataAddr, (int) cip->ephemLo, (int) cip->ephemHi) < 0)
1154 #endif
1155 {
1156 Error(cip, kDoPerror, "Could not bind the data socket");
1157 result = kErrBindDataSocket;
1158 cip->errNo = kErrBindDataSocket;
1159 goto bad;
1160 }
1161
1162 #ifdef NO_SIGNALS
1163 result = SConnect(dataSocket, &cip->servDataAddr, (int) cip->connTimeout);
1164 #else /* NO_SIGNALS */
1165 if (cip->connTimeout > 0)
1166 (void) alarm(cip->connTimeout);
1167
1168 result = connect(dataSocket, (struct sockaddr *) &cip->servDataAddr, (int) sizeof(cip->servDataAddr));
1169 if (cip->connTimeout > 0)
1170 (void) alarm(0);
1171 #endif /* NO_SIGNALS */
1172
1173 #ifdef NO_SIGNALS
1174 if (result == kTimeoutErr) {
1175 if (mode == kFallBackToSendPortMode) {
1176 Error(cip, kDontPerror, "Data connection timed out.\n");
1177 Error(cip, kDontPerror, "Falling back to PORT instead of PASV mode.\n");
1178 (void) closesocket(dataSocket);
1179 cip->hasPASV = kCommandNotAvailable;
1180 goto tryPort2;
1181 }
1182 Error(cip, kDontPerror, "Data connection timed out.\n");
1183 result = kErrConnectDataSocket;
1184 cip->errNo = kErrConnectDataSocket;
1185 } else
1186 #endif /* NO_SIGNALS */
1187
1188 if (result < 0) {
1189 #ifdef ECONNREFUSED
1190 if ((weirdPort > 0) && (errno == ECONNREFUSED)) {
1191 #elif defined(WSAECONNREFUSED)
1192 if ((weirdPort > 0) && (errno == WSAECONNREFUSED)) {
1193 #endif
1194 Error(cip, kDontPerror, "Server sent back a bogus port number.\nI will fall back to PORT instead of PASV mode.\n");
1195 if (mode == kFallBackToSendPortMode) {
1196 (void) closesocket(dataSocket);
1197 cip->hasPASV = kCommandNotAvailable;
1198 goto tryPort2;
1199 }
1200 result = kErrServerSentBogusPortNumber;
1201 cip->errNo = kErrServerSentBogusPortNumber;
1202 goto bad;
1203 }
1204 if (mode == kFallBackToSendPortMode) {
1205 Error(cip, kDoPerror, "connect failed.\n");
1206 Error(cip, kDontPerror, "Falling back to PORT instead of PASV mode.\n");
1207 (void) closesocket(dataSocket);
1208 cip->hasPASV = kCommandNotAvailable;
1209 goto tryPort2;
1210 }
1211 Error(cip, kDoPerror, "connect failed.\n");
1212 result = kErrConnectDataSocket;
1213 cip->errNo = kErrConnectDataSocket;
1214 goto bad;
1215 }
1216
1217 /* Need to do this so we can figure out which port the system
1218 * gave to us.
1219 */
1220 if ((result = GetSocketAddress(cip, dataSocket, &cip->ourDataAddr)) < 0)
1221 goto bad;
1222
1223 cip->dataPortMode = kPassiveMode;
1224 cip->hasPASV = kCommandAvailable;
1225 }
1226
1227 (void) SetLinger(cip, dataSocket, 1);
1228 (void) SetKeepAlive(cip, dataSocket);
1229
1230 #if defined(IP_TOS) && defined(IPTOS_THROUGHPUT)
1231 /* Data connection is a non-interactive data stream, so
1232 * high throughput is desired, at the expense of low
1233 * response time.
1234 */
1235 (void) SetTypeOfService(cip, dataSocket, IPTOS_THROUGHPUT);
1236 #endif
1237
1238 cip->dataSocket = dataSocket;
1239 return (0);
1240 bad:
1241 (void) closesocket(dataSocket);
1242 return (result);
1243 } /* OpenDataConnection */
1244
1245
1246
1247
1248 int
1249 AcceptDataConnection(const FTPCIPtr cip)
1250 {
1251 int newSocket;
1252 #ifndef NO_SIGNALS
1253 int len;
1254 #endif
1255 unsigned short remoteDataPort;
1256 unsigned short remoteCtrlPort;
1257
1258 /* If we did a PORT, we have some things to finish up.
1259 * If we did a PASV, we're ready to go.
1260 */
1261 if (cip->dataPortMode == kSendPortMode) {
1262 /* Accept will give us back the server's data address; at the
1263 * moment we don't do anything with it though.
1264 */
1265 memset(&cip->servDataAddr, 0, sizeof(cip->servDataAddr));
1266
1267 #ifdef NO_SIGNALS
1268 newSocket = SAccept(cip->dataSocket, &cip->servDataAddr, (int) cip->connTimeout);
1269 #else /* NO_SIGNALS */
1270 len = (int) sizeof(cip->servDataAddr);
1271 if (cip->connTimeout > 0)
1272 (void) alarm(cip->connTimeout);
1273 newSocket = accept(cip->dataSocket, (struct sockaddr *) &cip->servDataAddr, &len);
1274 if (cip->connTimeout > 0)
1275 (void) alarm(0);
1276 #endif /* NO_SIGNALS */
1277
1278 (void) closesocket(cip->dataSocket);
1279 if (newSocket < 0) {
1280 Error(cip, kDoPerror, "Could not accept a data connection.\n");
1281 cip->dataSocket = kClosedFileDescriptor;
1282 cip->errNo = kErrAcceptDataSocket;
1283 return (kErrAcceptDataSocket);
1284 }
1285
1286 if (cip->require20 != 0) {
1287 remoteDataPort = ntohs(cip->servDataAddr.sin_port);
1288 remoteCtrlPort = ntohs(cip->servCtlAddr.sin_port);
1289 if ((int) remoteDataPort != ((int) remoteCtrlPort - 1)) {
1290 Error(cip, kDontPerror, "Data connection did not originate on correct port!\n");
1291 (void) closesocket(newSocket);
1292 cip->dataSocket = kClosedFileDescriptor;
1293 cip->errNo = kErrAcceptDataSocket;
1294 return (kErrAcceptDataSocket);
1295 } else if (memcmp(&cip->servDataAddr.sin_addr.s_addr, &cip->servCtlAddr.sin_addr.s_addr, sizeof(cip->servDataAddr.sin_addr.s_addr)) != 0) {
1296 Error(cip, kDontPerror, "Data connection did not originate from remote server!\n");
1297 (void) closesocket(newSocket);
1298 cip->dataSocket = kClosedFileDescriptor;
1299 cip->errNo = kErrAcceptDataSocket;
1300 return (kErrAcceptDataSocket);
1301 }
1302 }
1303
1304 cip->dataSocket = newSocket;
1305 }
1306
1307 return (0);
1308 } /* AcceptDataConnection */
1309
1310
1311
1312
1313 void
1314 HangupOnServer(const FTPCIPtr cip)
1315 {
1316 /* Since we want to close both sides of the connection for each
1317 * socket, we can just have them closed with close() instead of
1318 * using shutdown().
1319 */
1320 CloseControlConnection(cip);
1321 CloseDataConnection(cip);
1322 } /* HangupOnServer */
1323
1324
1325
1326
1327 void
1328 SendTelnetInterrupt(const FTPCIPtr cip)
1329 {
1330 char msg[4];
1331
1332 /* 1. User system inserts the Telnet "Interrupt Process" (IP) signal
1333 * in the Telnet stream.
1334 */
1335
1336 if (cip->cout != NULL)
1337 (void) fflush(cip->cout);
1338
1339 msg[0] = (char) (unsigned char) IAC;
1340 msg[1] = (char) (unsigned char) IP;
1341 (void) send(cip->ctrlSocketW, msg, 2, 0);
1342
1343 /* 2. User system sends the Telnet "Sync" signal. */
1344 #if 1
1345 msg[0] = (char) (unsigned char) IAC;
1346 msg[1] = (char) (unsigned char) DM;
1347 if (send(cip->ctrlSocketW, msg, 2, MSG_OOB) != 2)
1348 Error(cip, kDoPerror, "Could not send an urgent message.\n");
1349 #else
1350 /* "Send IAC in urgent mode instead of DM because UNIX places oob mark
1351 * after urgent byte rather than before as now is protocol," says
1352 * the BSD ftp code.
1353 */
1354 msg[0] = (char) (unsigned char) IAC;
1355 if (send(cip->ctrlSocketW, msg, 1, MSG_OOB) != 1)
1356 Error(cip, kDoPerror, "Could not send an urgent message.\n");
1357 (void) fprintf(cip->cout, "%c", DM);
1358 (void) fflush(cip->cout);
1359 #endif
1360 } /* SendTelnetInterrupt */
1361
1362 /* eof FTP.c */