3 * Copyright (c) 1996-2001 Mike Gleason, NCEMRSoft.
8 #define _libncftp_ftp_c_
11 char gLibNcFTPVersion
[64] = kLibraryVersion
;
14 static char gNoSignalsMarker
[] = "@(#) LibNcFTP - NO_SIGNALS";
17 static int gGotSig
= 0;
19 static sigjmp_buf gCancelConnectJmp
;
21 static jmp_buf gCancelConnectJmp
;
22 #endif /* HAVE_SIGSETJMP */
24 #endif /* NO_SIGNALS */
28 static char gCopyright
[] = "@(#) LibNcFTP Copyright 1995-2000, by Mike Gleason. All rights reserved.";
36 # define accept Raccept
37 # define connect Rconnect
38 # define getsockname Rgetsockname
39 # define listen Rlisten
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.
52 static struct hostent
*
53 GetHostEntry(char *host
, struct in_addr
*ip_address
)
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).
62 ip
.s_addr
= inet_addr(host
);
63 if (ip
.s_addr
!= INADDR_NONE
) {
66 /* No IP address, so it must be a hostname, like ftp.wustl.edu. */
67 hp
= gethostbyname(host
);
69 (void) memcpy(&ip
.s_addr
, hp
->h_addr_list
[0], (size_t) hp
->h_length
);
71 if (ip_address
!= NULL
)
79 /* Makes every effort to return a fully qualified domain name. */
81 GetOurHostName(char *host
, size_t siz
)
84 /* You can hardcode in the name if this routine doesn't work
85 * the way you want it to.
87 Strncpy(host
, HOSTNAME
, siz
);
88 return (1); /* Success */
98 result
= gethostname(host
, (int) siz
);
99 if ((result
< 0) || (host
[0] == '\0')) {
103 if (strchr(host
, '.') != NULL
) {
104 /* gethostname returned full name (like "cse.unl.edu"), instead
105 * of just the node name (like "cse").
107 return (2); /* Success */
110 hp
= gethostbyname(host
);
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 */
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 */
130 /* Otherwise, we just have the node name. See if we can get the
131 * domain name ourselves.
134 (void) STRNCPY(domain
, DOMAINNAME
);
139 # if defined(HAVE_RES_INIT) && defined(HAVE__RES_DEFDNAME)
140 if (domain
[0] == '\0') {
142 if ((_res
.defdname
!= NULL
) && (_res
.defdname
[0] != '\0')) {
143 (void) STRNCPY(domain
, _res
.defdname
);
147 # endif /* HAVE_RES_INIT && HAVE__RES_DEFDNAME */
149 if (domain
[0] == '\0') {
154 fp
= fopen("/etc/resolv.conf", "r");
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");
162 continue; /* Impossible */
163 if (strcmp(tok
, "domain") == 0) {
164 tok
= strtok(NULL
, " \t\n\r");
166 continue; /* syntax error */
167 (void) STRNCPY(domain
, tok
);
175 #endif /* DOMAINNAME */
177 if (domain
[0] != '\0') {
178 /* Supposedly, it's legal for a domain name with
179 * a period at the end.
181 cp
= domain
+ strlen(domain
) - 1;
184 if (domain
[0] != '.')
185 (void) Strncat(host
, ".", siz
);
186 (void) Strncat(host
, domain
, siz
);
190 return(rc
); /* Success */
191 #endif /* !HOSTNAME */
192 } /* GetOurHostName */
197 CloseControlConnection(const FTPCIPtr cip
)
199 /* This will close each file, if it was open. */
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)
214 #endif /* NO_SIGNALS */
217 } /* CloseControlConnection */
222 GetSocketAddress(const FTPCIPtr cip
, int sockfd
, struct sockaddr_in
*saddr
)
224 int len
= (int) sizeof (struct sockaddr_in
);
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
;
233 } /* GetSocketAddress */
239 SetKeepAlive(const FTPCIPtr cip
, int sockfd
)
242 cip
->errNo
= kErrSetKeepAlive
;
243 return (kErrSetKeepAlive
);
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
);
255 #endif /* SO_KEEPALIVE */
262 SetLinger(const FTPCIPtr cip
, int sockfd
, int onoff
)
265 cip
->errNo
= kErrSetLinger
;
266 return (kErrSetLinger
);
272 li
.l_linger
= 120; /* 2 minutes, but system ignores field. */
277 /* Have the system make an effort to deliver any unsent data,
278 * even after we close the connection.
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
);
286 #endif /* SO_LINGER */
294 SetTypeOfService(const FTPCIPtr cip
, int sockfd
, int tosType
)
296 /* Specify to the router what type of connection this is, so it
297 * can prioritize packets.
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
);
305 } /* SetTypeOfService */
313 SetInlineOutOfBandData(const FTPCIPtr cip
, int sockfd
)
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
);
323 } /* SetInlineOutOfBandData */
324 #endif /* SO_OOBINLINE */
332 CancelConnect(int signum
)
335 #ifdef HAVE_SIGSETJMP
336 siglongjmp(gCancelConnectJmp
, 1);
338 longjmp(gCancelConnectJmp
, 1);
339 #endif /* HAVE_SIGSETJMP */
340 } /* CancelConnect */
342 #endif /* NO_SIGNALS */
347 OpenControlConnection(const FTPCIPtr cip
, char *host
, unsigned int port
)
349 struct in_addr ip_address
;
353 volatile int sockfd
= -1;
354 volatile int sock2fd
= -1;
356 char **volatile curaddr
;
358 char *volatile fhost
;
361 volatile FTPSigProc osigint
;
362 volatile FTPSigProc osigalrm
;
363 volatile FTPCIPtr vcip
;
365 #endif /* NO_SIGNALS */
366 const char *firstLine
, *secondLine
, *srvr
;
368 LIBNCFTP_USE_VAR(gLibNcFTPVersion
);
369 LIBNCFTP_USE_VAR(gCopyright
);
371 LIBNCFTP_USE_VAR(gNoSignalsMarker
);
372 #endif /* NO_SIGNALS */
374 if (cip
->firewallType
== kFirewallNotInUse
) {
378 fhost
= cip
->firewallHost
;
379 fport
= cip
->firewallPort
;
382 fport
= cip
->lip
->defaultPort
;
384 /* Since we're the client, we just have to get a socket() and
387 (void) ZERO(cip
->servCtlAddr
);
391 /* Make sure we use network byte-order. */
392 fport
= (unsigned int) htons((unsigned short) fport
);
394 cip
->servCtlAddr
.sin_port
= (unsigned short) fport
;
396 hp
= GetHostEntry(fhost
, &ip_address
);
399 /* Okay, no Host entry, but maybe we have a numeric address
400 * in ip_address we can try.
402 if (ip_address
.s_addr
== INADDR_NONE
) {
403 Error(cip
, kDontPerror
, "%s: unknown host.\n", fhost
);
404 cip
->errNo
= kErrHostUnknown
;
405 return (kErrHostUnknown
);
407 cip
->servCtlAddr
.sin_family
= AF_INET
;
408 cip
->servCtlAddr
.sin_addr
.s_addr
= ip_address
.s_addr
;
410 cip
->servCtlAddr
.sin_family
= hp
->h_addrtype
;
411 /* We'll fill in the rest of the structure below. */
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.
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.
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
);
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
437 (void) SetSockBufSize(sockfd
, cip
->ctrlSocketRBufSize
, cip
->ctrlSocketSBufSize
);
440 err
= SConnect(sockfd
, &cip
->servCtlAddr
, (int) cip
->connTimeout
);
444 (void) SClose(sockfd
, 3);
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
);
457 #ifdef HAVE_SIGSETJMP
458 sj
= sigsetjmp(gCancelConnectJmp
, 1);
460 sj
= setjmp(gCancelConnectJmp
);
461 #endif /* HAVE_SIGSETJMP */
464 /* Interrupted by a signal. */
465 (void) closesocket(sockfd
);
466 (void) signal(SIGINT
, (FTPSigProc
) osigint
);
467 if (vcip
->connTimeout
> 0) {
469 (void) signal(SIGALRM
, (FTPSigProc
) osigalrm
);
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
);
480 result
= vcip
->errNo
= kErrConnectMiscErr
;
481 Error(vcip
, kDontPerror
, "Connection attempt failed due to an unexpected signal (%d).\n", gGotSig
);
485 err
= connect(sockfd
, (struct sockaddr
*) &cip
->servCtlAddr
,
486 (int) sizeof (cip
->servCtlAddr
));
487 if (cip
->connTimeout
> 0) {
489 (void) signal(SIGALRM
, (FTPSigProc
) osigalrm
);
491 (void) signal(SIGINT
, (FTPSigProc
) osigint
);
496 (void) closesocket(sockfd
);
500 #endif /* NO_SIGNALS */
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.
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
);
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.
515 (void) memcpy(&cip
->servCtlAddr
.sin_addr
, *curaddr
, (size_t) hp
->h_length
);
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
522 (void) SetSockBufSize(sockfd
, cip
->ctrlSocketRBufSize
, cip
->ctrlSocketSBufSize
);
525 err
= SConnect(sockfd
, &cip
->servCtlAddr
, (int) cip
->connTimeout
);
530 (void) SClose(sockfd
, 3);
533 #else /* NO_SIGNALS */
535 osigint
= (volatile FTPSigProc
) signal(SIGINT
, CancelConnect
);
536 if (cip
->connTimeout
> 0) {
537 osigalrm
= (volatile FTPSigProc
) signal(SIGALRM
, CancelConnect
);
538 (void) alarm(cip
->connTimeout
);
542 #ifdef HAVE_SIGSETJMP
543 sj
= sigsetjmp(gCancelConnectJmp
, 1);
545 sj
= setjmp(gCancelConnectJmp
);
546 #endif /* HAVE_SIGSETJMP */
549 /* Interrupted by a signal. */
550 (void) closesocket(sockfd
);
551 (void) signal(SIGINT
, (FTPSigProc
) osigint
);
552 if (vcip
->connTimeout
> 0) {
554 (void) signal(SIGALRM
, (FTPSigProc
) osigalrm
);
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
);
565 result
= vcip
->errNo
= kErrConnectMiscErr
;
566 Error(vcip
, kDontPerror
, "Connection attempt failed due to an unexpected signal (%d).\n", gGotSig
);
570 err
= connect(sockfd
, (struct sockaddr
*) &cip
->servCtlAddr
,
571 (int) sizeof (cip
->servCtlAddr
));
572 if (cip
->connTimeout
> 0) {
574 (void) signal(SIGALRM
, (FTPSigProc
) osigalrm
);
576 (void) signal(SIGINT
, (FTPSigProc
) osigint
);
582 (void) closesocket(sockfd
);
585 #endif /* NO_SIGNALS */
590 /* Could not connect. Close up shop and go home. */
592 /* If possible, tell the caller if they should bother
593 * calling back later.
598 #elif defined(WSAENETDOWN)
603 #elif defined(WSAENETUNREACH)
608 #elif defined(WSAECONNABORTED)
609 case WSAECONNABORTED
:
613 #elif defined(WSAETIMEDOUT)
618 #elif defined(WSAEHOSTDOWN)
623 #elif defined(WSAECONNRESET)
626 Error(cip
, kDoPerror
, "Could not connect to %s -- try again later.\n", fhost
);
627 result
= cip
->errNo
= kErrConnectRetryableErr
;
631 #elif defined(WSAECONNREFUSED)
632 case WSAECONNREFUSED
:
634 Error(cip
, kDoPerror
, "Could not connect to %s.\n", fhost
);
635 result
= cip
->errNo
= kErrConnectRefused
;
638 Error(cip
, kDoPerror
, "Could not connect to %s.\n", fhost
);
639 result
= cip
->errNo
= kErrConnectMiscErr
;
644 /* Get our end of the socket address for later use. */
645 if ((result
= GetSocketAddress(cip
, sockfd
, &cip
->ourCtlAddr
)) < 0)
649 /* We want Out-of-band data to appear in the regular stream,
650 * since we can handle TELNET.
652 (void) SetInlineOutOfBandData(cip
, sockfd
);
654 (void) SetKeepAlive(cip
, sockfd
);
655 (void) SetLinger(cip
, sockfd
, 0); /* Don't need it for ctrl. */
657 #if defined(IP_TOS) && defined(IPTOS_LOWDELAY)
658 /* Control connection is somewhat interactive, so quick response
661 (void) SetTypeOfService(cip
, sockfd
, IPTOS_LOWDELAY
);
665 cip
->ctrlSocketR
= sockfd
;
666 cip
->ctrlSocketW
= sockfd
;
669 sock2fd
= kClosedFileDescriptor
;
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");
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");
685 /* Now setup the FILE pointers for use with the Std I/O library
688 if ((cip
->cin
= fdopen(sockfd
, "r")) == NULL
) {
689 result
= kErrFdopenR
;
690 cip
->errNo
= kErrFdopenR
;
691 Error(cip
, kDoPerror
, "Could not fdopen.\n");
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
;
704 cip
->ctrlSocketR
= sockfd
;
705 cip
->ctrlSocketW
= sockfd
;
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.
711 #ifdef HAVE_SETLINEBUF
712 setlinebuf(cip
->cin
);
713 setlinebuf(cip
->cout
);
715 (void) SETVBUF(cip
->cin
, NULL
, _IOLBF
, (size_t) BUFSIZ
);
716 (void) SETVBUF(cip
->cout
, NULL
, _IOLBF
, (size_t) BUFSIZ
);
718 #endif /* NO_SIGNALS */
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);
724 (void) STRNCPY(cip
->ip
, inet_ntoa(cip
->servCtlAddr
.sin_addr
));
726 if ((hp
== NULL
) || (hp
->h_name
== NULL
))
727 (void) STRNCPY(cip
->actualHost
, fhost
);
729 (void) STRNCPY(cip
->actualHost
, (char *) hp
->h_name
);
731 /* Read the startup message from the server. */
734 Error(cip
, kDontPerror
, "Malloc failed.\n");
735 cip
->errNo
= kErrMallocFailed
;
740 result
= GetResponse(cip
, rp
);
741 if ((result
< 0) && (rp
->msg
.first
== NULL
)) {
744 if (rp
->msg
.first
!= NULL
) {
745 cip
->serverType
= kServerTypeUnknown
;
747 firstLine
= rp
->msg
.first
->line
;
749 if (rp
->msg
.first
->next
!= NULL
)
750 secondLine
= rp
->msg
.first
->next
->line
;
752 if (strstr(firstLine
, "Version wu-") != NULL
) {
753 cip
->serverType
= kServerTypeWuFTPd
;
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
;
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
;
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
;
776 } else if (STRNEQ("FTP-Max", firstLine
, 7)) {
777 cip
->serverType
= kServerTypeFTP_Max
;
779 } else if (strstr(firstLine
, "Roxen") != NULL
) {
780 cip
->serverType
= kServerTypeRoxen
;
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
;
791 PrintF(cip
, "Remote server is running %s.\n", srvr
);
793 /* Do the application's connect message callback, if present. */
794 if ((cip
->onConnectMsgProc
!= 0) && (rp
->codeType
< 4))
795 (*cip
->onConnectMsgProc
)(cip
, rp
);
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
803 DoneWithResponse(cip
, rp
);
804 result
= kErrConnectRetryableErr
;
805 Error(cip
, kDontPerror
, "Server hungup immediately after connect.\n");
806 cip
->errNo
= kErrConnectRetryableErr
;
809 if (result
< 0) /* Some other error occurred during connect message */
812 DoneWithResponse(cip
, rp
);
817 (void) closesocket(sockfd
);
819 (void) closesocket(sock2fd
);
820 CloseFile(&cip
->cin
);
821 CloseFile(&cip
->cout
);
822 cip
->ctrlSocketR
= kClosedFileDescriptor
;
823 cip
->ctrlSocketW
= kClosedFileDescriptor
;
825 } /* OpenControlConnection */
831 CloseDataConnection(const FTPCIPtr cip
)
833 if (cip
->dataSocket
!= kClosedFileDescriptor
) {
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)
842 #endif /* NO_SIGNALS */
843 cip
->dataSocket
= kClosedFileDescriptor
;
845 memset(&cip
->ourDataAddr
, 0, sizeof(cip
->ourDataAddr
));
846 memset(&cip
->servDataAddr
, 0, sizeof(cip
->servDataAddr
));
847 } /* CloseDataConnection */
853 SetStartOffset(const FTPCIPtr cip
, longest_int restartPt
)
858 if (restartPt
!= (longest_int
) 0) {
861 Error(cip
, kDontPerror
, "Malloc failed.\n");
862 cip
->errNo
= kErrMallocFailed
;
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
,
874 result
= RCmd(cip
, rp
, "REST %ld", (long) restartPt
);
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
);
888 DoneWithResponse(cip
, rp
);
889 cip
->errNo
= kErrSetStartPoint
;
890 return (kErrSetStartPoint
);
894 } /* SetStartOffset */
899 SendPort(const FTPCIPtr cip
, struct sockaddr_in
*saddr
)
907 Error(cip
, kDontPerror
, "Malloc failed.\n");
908 cip
->errNo
= kErrMallocFailed
;
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)
917 /* Need to tell the other side which host (the address) and
918 * which process (port) on that host to send data to.
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]));
925 } else if (result
!= 2) {
926 /* A 500'ish response code means the PORT command failed. */
927 DoneWithResponse(cip
, rp
);
928 cip
->errNo
= kErrPORTFailed
;
931 DoneWithResponse(cip
, rp
);
939 Passive(const FTPCIPtr cip
, struct sockaddr_in
*saddr
, int *weird
)
949 Error(cip
, kDontPerror
, "Malloc failed.\n");
950 cip
->errNo
= kErrMallocFailed
;
954 result
= RCmd(cip
, rp
, "PASV");
958 if (rp
->codeType
!= 2) {
959 /* Didn't understand or didn't want passive port selection. */
960 cip
->errNo
= result
= kErrPASVFailed
;
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).
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.
973 for (cp
= rp
->msg
.first
->line
; ; cp
++) {
975 Error(cip
, kDontPerror
, "Cannot parse PASV response: %s\n", rp
->msg
.first
->line
);
978 if (isdigit((int) *cp
))
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
);
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.
993 if ((i
[j
] < 0) || (i
[j
] > 255))
995 n
[j
] = (unsigned char) (i
[j
] & 0xff);
998 (void) memcpy(&saddr
->sin_addr
, &n
[0], (size_t) 4);
999 (void) memcpy(&saddr
->sin_port
, &n
[4], (size_t) 2);
1003 DoneWithResponse(cip
, rp
);
1011 BindToEphemeralPortNumber(int sockfd
, struct sockaddr_in
*addrp
, int ephemLo
, int ephemHi
)
1016 unsigned short port
;
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
1024 addrp
->sin_port
= 0; /* Let system pick one. */
1026 result
= bind(sockfd
, (struct sockaddr
*) addrp
, sizeof(struct sockaddr_in
));
1028 rangesize
= (int) ((int) ephemHi
- (int) ephemLo
);
1030 for (i
=0; i
<10; i
++) {
1031 port
= (unsigned short) (((int) rand() % rangesize
) + (int) ephemLo
);
1032 addrp
->sin_port
= port
;
1034 result
= bind(sockfd
, (struct sockaddr
*) addrp
, sizeof(struct sockaddr_in
));
1038 /* This next line is just fodder to
1039 * shut the compiler up about variable
1042 && (gCopyright
[0] != '\0'))
1047 } /* BindToEphemeralPortNumber */
1053 OpenDataConnection(const FTPCIPtr cip
, int mode
)
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.
1067 CloseDataConnection(cip
); /* In case we didn't before... */
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
;
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
1082 (void) SetSockBufSize(dataSocket
, cip
->dataSocketRBufSize
, cip
->dataSocketSBufSize
);
1084 if ((cip
->hasPASV
== kCommandNotAvailable
) || (mode
== kSendPortMode
)) {
1086 cip
->ourDataAddr
= cip
->ourCtlAddr
;
1087 cip
->ourDataAddr
.sin_family
= AF_INET
;
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)
1095 if (BindToEphemeralPortNumber(dataSocket
, &cip
->ourDataAddr
, (int) cip
->ephemLo
, (int) cip
->ephemHi
) < 0)
1098 Error(cip
, kDoPerror
, "Could not bind the data socket");
1099 result
= kErrBindDataSocket
;
1100 cip
->errNo
= kErrBindDataSocket
;
1104 /* Need to do this so we can figure out which port the system
1107 if ((result
= GetSocketAddress(cip
, dataSocket
, &cip
->ourDataAddr
)) < 0)
1110 if (listen(dataSocket
, 1) < 0) {
1111 Error(cip
, kDoPerror
, "listen failed");
1112 result
= kErrListenDataSocket
;
1113 cip
->errNo
= kErrListenDataSocket
;
1117 if ((result
= SendPort(cip
, &cip
->ourDataAddr
)) < 0)
1120 cip
->dataPortMode
= kSendPortMode
;
1122 /* Passive mode. Let the other side decide where to send. */
1124 cip
->servDataAddr
= cip
->servCtlAddr
;
1125 cip
->servDataAddr
.sin_family
= AF_INET
;
1126 cip
->ourDataAddr
= cip
->ourCtlAddr
;
1127 cip
->ourDataAddr
.sin_family
= AF_INET
;
1129 if (Passive(cip
, &cip
->servDataAddr
, &weirdPort
) < 0) {
1130 Error(cip
, kDontPerror
, "Passive mode refused.\n");
1131 cip
->hasPASV
= kCommandNotAvailable
;
1133 /* We can try using regular PORT commands, which are required
1134 * by all FTP protocol compliant programs, if you said so.
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.
1140 if (mode
== kFallBackToSendPortMode
)
1142 result
= kErrPassiveModeFailed
;
1143 cip
->errNo
= kErrPassiveModeFailed
;
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)
1153 if (BindToEphemeralPortNumber(dataSocket
, &cip
->ourDataAddr
, (int) cip
->ephemLo
, (int) cip
->ephemHi
) < 0)
1156 Error(cip
, kDoPerror
, "Could not bind the data socket");
1157 result
= kErrBindDataSocket
;
1158 cip
->errNo
= kErrBindDataSocket
;
1163 result
= SConnect(dataSocket
, &cip
->servDataAddr
, (int) cip
->connTimeout
);
1164 #else /* NO_SIGNALS */
1165 if (cip
->connTimeout
> 0)
1166 (void) alarm(cip
->connTimeout
);
1168 result
= connect(dataSocket
, (struct sockaddr
*) &cip
->servDataAddr
, (int) sizeof(cip
->servDataAddr
));
1169 if (cip
->connTimeout
> 0)
1171 #endif /* 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
;
1182 Error(cip
, kDontPerror
, "Data connection timed out.\n");
1183 result
= kErrConnectDataSocket
;
1184 cip
->errNo
= kErrConnectDataSocket
;
1186 #endif /* NO_SIGNALS */
1190 if ((weirdPort
> 0) && (errno
== ECONNREFUSED
)) {
1191 #elif defined(WSAECONNREFUSED)
1192 if ((weirdPort
> 0) && (errno
== WSAECONNREFUSED
)) {
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
;
1200 result
= kErrServerSentBogusPortNumber
;
1201 cip
->errNo
= kErrServerSentBogusPortNumber
;
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
;
1211 Error(cip
, kDoPerror
, "connect failed.\n");
1212 result
= kErrConnectDataSocket
;
1213 cip
->errNo
= kErrConnectDataSocket
;
1217 /* Need to do this so we can figure out which port the system
1220 if ((result
= GetSocketAddress(cip
, dataSocket
, &cip
->ourDataAddr
)) < 0)
1223 cip
->dataPortMode
= kPassiveMode
;
1224 cip
->hasPASV
= kCommandAvailable
;
1227 (void) SetLinger(cip
, dataSocket
, 1);
1228 (void) SetKeepAlive(cip
, dataSocket
);
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
1235 (void) SetTypeOfService(cip
, dataSocket
, IPTOS_THROUGHPUT
);
1238 cip
->dataSocket
= dataSocket
;
1241 (void) closesocket(dataSocket
);
1243 } /* OpenDataConnection */
1249 AcceptDataConnection(const FTPCIPtr cip
)
1255 unsigned short remoteDataPort
;
1256 unsigned short remoteCtrlPort
;
1258 /* If we did a PORT, we have some things to finish up.
1259 * If we did a PASV, we're ready to go.
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.
1265 memset(&cip
->servDataAddr
, 0, sizeof(cip
->servDataAddr
));
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)
1276 #endif /* NO_SIGNALS */
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
);
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
);
1304 cip
->dataSocket
= newSocket
;
1308 } /* AcceptDataConnection */
1314 HangupOnServer(const FTPCIPtr cip
)
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
1320 CloseControlConnection(cip
);
1321 CloseDataConnection(cip
);
1322 } /* HangupOnServer */
1328 SendTelnetInterrupt(const FTPCIPtr cip
)
1332 /* 1. User system inserts the Telnet "Interrupt Process" (IP) signal
1333 * in the Telnet stream.
1336 if (cip
->cout
!= NULL
)
1337 (void) fflush(cip
->cout
);
1339 msg
[0] = (char) (unsigned char) IAC
;
1340 msg
[1] = (char) (unsigned char) IP
;
1341 (void) send(cip
->ctrlSocketW
, msg
, 2, 0);
1343 /* 2. User system sends the Telnet "Sync" signal. */
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");
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
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
);
1360 } /* SendTelnetInterrupt */