2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
14 * Turned inside out. Now returns xfers as new file ids, not as a special
17 * $FreeBSD: src/lib/libftpio/ftpio.c,v 1.33.2.4 2002/07/25 15:25:32 ume Exp $
18 * $DragonFly: src/lib/libftpio/ftpio.c,v 1.8 2005/07/23 20:23:06 joerg Exp $
22 #include <sys/param.h>
23 #include <sys/socket.h>
25 #include <netinet/in.h>
27 #include <arpa/inet.h>
49 /* How to see by a given code whether or not the connection has timed out */
50 #define FTP_TIMEOUT(code) (FtpTimedOut || code == FTP_TIMED_OUT)
52 /* Internal routines - deal only with internal FTP_t type */
53 static FTP_t
ftp_new(void);
54 static void check_passive(FILE *fp
);
55 static int ftp_read_method(void *n
, char *buf
, int nbytes
);
56 static int ftp_write_method(void *n
, const char *buf
, int nbytes
);
57 static int ftp_close_method(void *n
);
58 static int writes(int fd
, const char *s
);
59 static char *get_a_line(FTP_t ftp
);
60 static int get_a_number(FTP_t ftp
, char **q
);
61 static int botch(const char *func
, const char *botch_state
);
62 static int cmd(FTP_t ftp
, const char *fmt
, ...) __printflike(2, 3);
63 static int ftp_login_session(FTP_t ftp
, const char *host
, int af
,
64 const char *user
, const char *passwd
,
65 int port
, int verbose
);
66 static int ftp_file_op(FTP_t ftp
, const char *operation
, const char *file
,
67 FILE **fp
, const char *mode
, off_t
*seekto
);
68 static int ftp_close(FTP_t ftp
);
69 static int get_url_info(const char *url_in
, char *host_ret
,
70 size_t host_len
, int *port_ret
,
71 char *name_ret
, size_t name_len
);
72 static void ftp_timeout(int sig
);
73 static void ftp_set_timeout(void);
74 static void ftp_clear_timeout(void);
75 static void ai_unmapped(struct addrinfo
*);
77 int networkInit(void);
80 /* Global status variable - ick */
83 /* FTP happy status codes */
84 #define FTP_GENERALLY_HAPPY 200
85 #define FTP_ASCII_HAPPY FTP_GENERALLY_HAPPY
86 #define FTP_BINARY_HAPPY FTP_GENERALLY_HAPPY
87 #define FTP_PORT_HAPPY FTP_GENERALLY_HAPPY
88 #define FTP_HAPPY_COMMENT 220
89 #define FTP_QUIT_HAPPY 221
90 #define FTP_TRANSFER_HAPPY 226
91 #define FTP_PASSIVE_HAPPY 227
92 #define FTP_LPASSIVE_HAPPY 228
93 #define FTP_EPASSIVE_HAPPY 229
94 #define FTP_CHDIR_HAPPY 250
96 /* FTP unhappy status codes */
97 #define FTP_TIMED_OUT 421
99 /* Placeholder in case we want to do any pre-init stuff at some point */
103 return SUCCESS
; /* XXX dummy function for now XXX */
106 /* Check a return code with some lenience for back-dated garbage that might be in the buffer */
108 check_code(FTP_t ftp
, int var
, int preferred
)
112 if (var
== preferred
)
114 else if (var
== FTP_TRANSFER_HAPPY
) /* last operation succeeded */
115 var
= get_a_number(ftp
, NULL
);
116 else if (var
== FTP_HAPPY_COMMENT
) /* chit-chat */
117 var
= get_a_number(ftp
, NULL
);
118 else if (var
== FTP_GENERALLY_HAPPY
) /* general success code */
119 var
= get_a_number(ftp
, NULL
);
130 FTP_t ftp
= fcookie(fp
);
135 i
= cmd(ftp
, "TYPE A");
136 if (i
< 0 || check_code(ftp
, i
, FTP_ASCII_HAPPY
))
138 ftp
->is_binary
= FALSE
;
145 FTP_t ftp
= fcookie(fp
);
150 i
= cmd(ftp
, "TYPE I");
151 if (i
< 0 || check_code(ftp
, i
, FTP_BINARY_HAPPY
))
153 ftp
->is_binary
= TRUE
;
157 ftpVerbose(FILE *fp
, int status
)
159 FTP_t ftp
= fcookie(fp
);
160 ftp
->is_verbose
= status
;
164 ftpChdir(FILE *fp
, char *dir
)
167 FTP_t ftp
= fcookie(fp
);
169 i
= cmd(ftp
, "CWD %s", dir
);
170 if (i
< 0 || check_code(ftp
, i
, FTP_CHDIR_HAPPY
))
178 FTP_t ftp
= fcookie(fp
);
183 ftpErrString(int error
)
188 return("connection in wrong state");
190 /* XXX soon UNIX errnos will catch up with FTP protocol errnos */
191 return strerror(error
);
192 for (k
= 0; k
< ftpErrListLength
; k
++)
193 if (ftpErrList
[k
].num
== error
)
194 return(ftpErrList
[k
].string
);
195 return("Unknown error");
199 ftpGetSize(FILE *fp
, const char *name
)
202 char p
[BUFSIZ
], *cp
, *ep
;
203 FTP_t ftp
= fcookie(fp
);
207 if ((size_t)snprintf(p
, sizeof(p
), "SIZE %s\r\n", name
) >= sizeof(p
))
210 fprintf(stderr
, "Sending %s", p
);
211 if (writes(ftp
->fd_ctrl
, p
))
213 i
= get_a_number(ftp
, &cp
);
214 if (check_code(ftp
, i
, 213))
217 errno
= 0; /* to check for ERANGE */
218 size
= (off_t
)strtoq(cp
, &ep
, 10);
219 if (*ep
!= '\0' || errno
== ERANGE
)
225 ftpGetModtime(FILE *fp
, const char *name
)
229 time_t t0
= time (0);
230 FTP_t ftp
= fcookie(fp
);
234 if ((size_t)snprintf(p
, sizeof(p
), "MDTM %s\r\n", name
) >= sizeof(p
))
237 fprintf(stderr
, "Sending %s", p
);
238 if (writes(ftp
->fd_ctrl
, p
))
240 i
= get_a_number(ftp
, &cp
);
241 if (check_code(ftp
, i
, 213))
243 while (*cp
&& !isdigit(*cp
))
247 t0
= localtime (&t0
)->tm_gmtoff
;
248 sscanf(cp
, "%04d%02d%02d%02d%02d%02d", &t
.tm_year
, &t
.tm_mon
, &t
.tm_mday
, &t
.tm_hour
, &t
.tm_min
, &t
.tm_sec
);
258 ftpGet(FILE *fp
, const char *file
, off_t
*seekto
)
261 FTP_t ftp
= fcookie(fp
);
264 if (ftpBinary(fp
) != SUCCESS
)
267 if (ftp_file_op(ftp
, "RETR", file
, &fp2
, "r", seekto
) == SUCCESS
)
272 /* Returns a standard FILE pointer type representing an open control connection */
274 ftpLogin(const char *host
, const char *user
, const char *passwd
, int port
,
275 int verbose
, int *retcode
)
278 return ftpLoginAf(host
, AF_UNSPEC
, user
, passwd
, port
, verbose
, retcode
);
280 return ftpLoginAf(host
, AF_INET
, user
, passwd
, port
, verbose
, retcode
);
285 ftpLoginAf(const char *host
, int af
, const char *user
, const char *passwd
,
286 int port
, int verbose
, int *retcode
)
293 if (networkInit() != SUCCESS
)
298 if (n
&& ftp_login_session(n
, host
, af
, user
, passwd
, port
, verbose
) == SUCCESS
) {
299 fp
= funopen(n
, ftp_read_method
, ftp_write_method
, NULL
, ftp_close_method
); /* BSD 4.4 function! */
303 *retcode
= (FtpTimedOut
? FTP_TIMED_OUT
: -1);
304 /* Poor attempt at mapping real errnos to FTP error codes */
305 else switch(n
->error
) {
307 *retcode
= FTP_TIMED_OUT
; /* Actually no such host, but we have no way of saying that. :-( */
311 *retcode
= FTP_TIMED_OUT
;
323 ftpPut(FILE *fp
, const char *file
)
326 FTP_t ftp
= fcookie(fp
);
329 if (ftp_file_op(ftp
, "STOR", file
, &fp2
, "w", NULL
) == SUCCESS
)
335 ftpPassive(FILE *fp
, int st
)
337 FTP_t ftp
= fcookie(fp
);
339 ftp
->is_passive
= !!st
; /* normalize "st" to zero or one */
344 ftpGetURL(const char *url
, const char *user
, const char *passwd
, int *retcode
)
347 return ftpGetURLAf(url
, AF_UNSPEC
, user
, passwd
, retcode
);
349 return ftpGetURLAf(url
, AF_INET
, user
, passwd
, retcode
);
354 ftpGetURLAf(const char *url
, int af
, const char *user
, const char *passwd
,
357 char host
[255], name
[255];
360 static FILE *fp
= NULL
;
361 static char *prev_host
;
365 if (get_url_info(url
, host
, sizeof(host
), &port
, name
,
366 sizeof(name
)) == SUCCESS
) {
367 if (fp
&& prev_host
) {
368 if (!strcmp(prev_host
, host
)) {
369 /* Try to use cached connection */
370 fp2
= ftpGet(fp
, name
, NULL
);
372 /* Connection timed out or was no longer valid */
381 /* It's a different host now, flush old */
387 fp
= ftpLoginAf(host
, af
, user
, passwd
, port
, 0, retcode
);
389 fp2
= ftpGet(fp
, name
, NULL
);
391 /* Connection timed out or was no longer valid */
393 *retcode
= ftpErrno(fp
);
398 prev_host
= strdup(host
);
406 ftpPutURL(const char *url
, const char *user
, const char *passwd
, int *retcode
)
409 return ftpPutURLAf(url
, AF_UNSPEC
, user
, passwd
, retcode
);
411 return ftpPutURLAf(url
, AF_INET
, user
, passwd
, retcode
);
417 ftpPutURLAf(const char *url
, int af
, const char *user
, const char *passwd
,
420 char host
[255], name
[255];
422 static FILE *fp
= NULL
;
427 if (fp
) { /* Close previous managed connection */
431 if (get_url_info(url
, host
, sizeof(host
), &port
,
432 name
, sizeof(name
)) == SUCCESS
) {
433 fp
= ftpLoginAf(host
, af
, user
, passwd
, port
, 0, retcode
);
435 fp2
= ftpPut(fp
, name
);
438 *retcode
= ftpErrno(fp
);
448 /* Internal workhorse function for dissecting URLs. Takes a URL as the first argument and returns the
449 result of such disection in the host, user, passwd, port and name variables. */
451 get_url_info(const char *url_in
, char *host_ret
, size_t host_len
,
452 int *port_ret
, char *name_ret
, size_t name_len
)
454 char *name
, *host
, *cp
, url
[BUFSIZ
];
458 /* XXX add http:// here or somewhere reasonable at some point XXX */
459 if (strncmp("ftp://", url_in
, 6) != 0)
461 /* We like to stomp a lot on the URL string in dissecting it, so copy it first */
462 strncpy(url
, url_in
, BUFSIZ
);
464 if ((cp
= index(host
, ':')) != NULL
) {
466 port
= strtol(cp
, 0, 0);
469 port
= 0; /* use default */
473 if ((name
= index(cp
? cp
: host
, '/')) != NULL
)
476 if (strlen(host
) >= host_len
)
478 strcpy(host_ret
, host
);
480 if (name
&& name_ret
) {
481 if (strlen(name
) >= name_len
)
483 strcpy(name_ret
, name
);
493 ftp
= (FTP_t
)malloc(sizeof *ftp
);
496 memset(ftp
, 0, sizeof *ftp
);
498 ftp
->con_state
= init
;
499 ftp
->is_binary
= FALSE
;
500 ftp
->is_passive
= FALSE
;
501 ftp
->is_verbose
= FALSE
;
507 ftp_read_method(void *vp
, char *buf
, int nbytes
)
513 i
= (fd
>= 0) ? read(fd
, buf
, nbytes
) : EOF
;
518 ftp_write_method(void *vp
, const char *buf
, int nbytes
)
524 i
= (fd
>= 0) ? write(fd
, buf
, nbytes
) : EOF
;
529 ftp_close_method(void *n
)
533 i
= ftp_close((FTP_t
)n
);
539 * This function checks whether the FTP_PASSIVE_MODE environment
540 * variable is set, and, if so, enforces the desired mode.
543 check_passive(FILE *fp
)
545 const char *cp
= getenv("FTP_PASSIVE_MODE");
548 ftpPassive(fp
, strncasecmp(cp
, "no", 2));
552 ftp_timeout(int sig __unused
)
555 /* Debug("ftp_pkg: ftp_timeout called - operation timed out"); */
559 ftp_set_timeout(void)
561 struct sigaction
new;
566 sigemptyset(&new.sa_mask
);
568 new.sa_handler
= ftp_timeout
;
569 sigaction(SIGALRM
, &new, NULL
);
570 cp
= getenv("FTP_TIMEOUT");
571 if (!cp
|| !(ival
= atoi(cp
)))
577 ftp_clear_timeout(void)
579 struct sigaction
new;
582 sigemptyset(&new.sa_mask
);
584 new.sa_handler
= SIG_DFL
;
585 sigaction(SIGALRM
, &new, NULL
);
589 writes(int fd
, const char *s
)
591 int n
, i
= strlen(s
);
596 if (FtpTimedOut
|| i
!= n
)
602 get_a_line(FTP_t ftp
)
604 static char buf
[BUFSIZ
];
607 /* Debug("ftp_pkg: trying to read a line from %d", ftp->fd_ctrl); */
608 for(i
= 0; i
< BUFSIZ
;) {
610 j
= read(ftp
->fd_ctrl
, buf
+ i
, 1);
612 if (FtpTimedOut
|| j
!= 1)
614 if (buf
[i
] == '\r' || buf
[i
] == '\n') {
618 if (ftp
->is_verbose
== TRUE
)
619 fprintf(stderr
, "%s\n",buf
+4);
624 /* Debug("ftp_pkg: read string \"%s\" from %d", buf, ftp->fd_ctrl); */
629 get_a_number(FTP_t ftp
, char **q
)
639 return FTP_TIMED_OUT
;
642 if (!(isdigit(p
[0]) && isdigit(p
[1]) && isdigit(p
[2])))
644 if (i
== -1 && p
[3] == '-') {
648 if (p
[3] != ' ' && p
[3] != '\t')
653 /* Debug("ftp_pkg: read reply %d from server (%s)", j, p); */
657 /* Debug("ftp_pkg: read reply %d from server (%s)", j, p); */
669 if (ftp
->con_state
== isopen
) {
670 ftp
->con_state
= quit
;
671 /* If last operation timed out, don't try to quit - just close */
672 if (ftp
->error
!= FTP_TIMED_OUT
)
673 i
= cmd(ftp
, "QUIT");
676 if (!check_code(ftp
, i
, FTP_QUIT_HAPPY
))
681 else if (ftp
->con_state
== quit
)
687 botch(const char *func __unused
, const char *botch_state __unused
)
689 /* Debug("ftp_pkg: botch: %s(%s)", func, botch_state); */
694 cmd(FTP_t ftp
, const char *fmt
, ...)
701 if ((size_t)vsnprintf(p
, sizeof p
- 2, fmt
, ap
) >= sizeof(p
) - 2)
705 if (ftp
->con_state
== init
)
706 return botch("cmd", "open");
710 fprintf(stderr
, "Sending: %s", p
);
711 if (writes(ftp
->fd_ctrl
, p
)) {
713 return FTP_TIMED_OUT
;
716 while ((i
= get_a_number(ftp
, NULL
)) == FTP_HAPPY_COMMENT
);
721 ftp_login_session(FTP_t ftp
, const char *host
, int af
,
722 const char *user
, const char *passwd
, int port
, int verbose
)
725 struct addrinfo hints
, *res
, *res0
;
730 if (networkInit() != SUCCESS
)
733 if (ftp
->con_state
!= init
) {
748 if ((size_t)snprintf(pbuf
, sizeof(pbuf
), "%d", port
) >= sizeof(pbuf
))
750 memset(&hints
, 0, sizeof(hints
));
751 hints
.ai_family
= af
;
752 hints
.ai_socktype
= SOCK_STREAM
;
753 hints
.ai_protocol
= 0;
754 err
= getaddrinfo(host
, pbuf
, &hints
, &res0
);
761 for (res
= res0
; res
; res
= res
->ai_next
) {
763 ftp
->addrtype
= res
->ai_family
;
765 if ((s
= socket(res
->ai_family
, res
->ai_socktype
,
766 res
->ai_protocol
)) < 0)
769 if (connect(s
, res
->ai_addr
, res
->ai_addrlen
) < 0) {
784 ftp
->con_state
= isopen
;
785 ftp
->is_verbose
= verbose
;
787 i
= cmd(ftp
, "USER %s", user
);
788 if (i
>= 300 && i
< 400)
789 i
= cmd(ftp
, "PASS %s", passwd
);
790 if (i
>= 299 || i
< 0) {
800 ftp_file_op(FTP_t ftp
, const char *operation
, const char *file
, FILE **fp
,
801 const char *mode
, off_t
*seekto
)
805 unsigned char addr
[64];
807 struct sockaddr_in sin4
;
808 struct sockaddr_in6 sin6
;
816 if (ftp
->con_state
!= isopen
)
817 return botch("ftp_file_op", "open");
819 if ((s
= socket(ftp
->addrtype
, SOCK_STREAM
, 0)) < 0) {
824 if (ftp
->is_passive
) {
825 if (ftp
->addrtype
== AF_INET
) {
827 fprintf(stderr
, "Sending PASV\n");
828 if (writes(ftp
->fd_ctrl
, "PASV\r\n")) {
831 ftp
->error
= FTP_TIMED_OUT
;
832 return FTP_TIMED_OUT
;
834 i
= get_a_number(ftp
, &q
);
835 if (check_code(ftp
, i
, FTP_PASSIVE_HAPPY
)) {
842 fprintf(stderr
, "Sending EPSV\n");
843 if (writes(ftp
->fd_ctrl
, "EPSV\r\n")) {
846 ftp
->error
= FTP_TIMED_OUT
;
847 return FTP_TIMED_OUT
;
849 i
= get_a_number(ftp
, &q
);
850 if (check_code(ftp
, i
, FTP_EPASSIVE_HAPPY
)) {
852 fprintf(stderr
, "Sending LPSV\n");
853 if (writes(ftp
->fd_ctrl
, "LPSV\r\n")) {
856 ftp
->error
= FTP_TIMED_OUT
;
857 return FTP_TIMED_OUT
;
859 i
= get_a_number(ftp
, &q
);
860 if (check_code(ftp
, i
, FTP_LPASSIVE_HAPPY
)) {
868 if (strcmp(cmdstr
, "PASV") == 0 || strcmp(cmdstr
, "LPSV") == 0) {
869 while (*q
&& !isdigit(*q
))
876 l
= (ftp
->addrtype
== AF_INET
? 6 : 21);
877 for (i
= 0; i
< l
; i
++) {
879 addr
[i
] = strtol(q
, &q
, 10);
882 sin
.sin4
.sin_family
= ftp
->addrtype
;
883 if (ftp
->addrtype
== AF_INET6
) {
884 sin
.sin6
.sin6_len
= sizeof(struct sockaddr_in6
);
885 bcopy(addr
+ 2, (char *)&sin
.sin6
.sin6_addr
, 16);
886 bcopy(addr
+ 19, (char *)&sin
.sin6
.sin6_port
, 2);
888 sin
.sin4
.sin_len
= sizeof(struct sockaddr_in
);
889 bcopy(addr
, (char *)&sin
.sin4
.sin_addr
, 4);
890 bcopy(addr
+ 4, (char *)&sin
.sin4
.sin_port
, 2);
892 } else if (strcmp(cmdstr
, "EPSV") == 0) {
895 while (*q
&& *q
!= '(') /* ) */
902 if (sscanf(q
, "%c%c%c%d%c", &addr
[0], &addr
[1], &addr
[2],
903 &port
, &addr
[3]) != 5
904 || addr
[0] != addr
[1] || addr
[0] != addr
[2] || addr
[0] != addr
[3]) {
908 sinlen
= sizeof(sin
);
909 if (getpeername(ftp
->fd_ctrl
, (struct sockaddr
*)&sin
, &sinlen
) < 0) {
913 switch (sin
.sin4
.sin_family
) {
915 sin
.sin4
.sin_port
= htons(port
);
918 sin
.sin6
.sin6_port
= htons(port
);
926 if (connect(s
, (struct sockaddr
*)&sin
, sin
.sin4
.sin_len
) < 0) {
931 if (seekto
&& *seekto
) {
932 i
= cmd(ftp
, "REST %" PRId64
, *seekto
);
933 if (i
< 0 || FTP_TIMEOUT(i
)) {
940 i
= cmd(ftp
, "%s %s", operation
, file
);
941 if (i
< 0 || i
> 299) {
946 *fp
= fdopen(s
, mode
);
951 #ifdef IPV6_PORTRANGE
952 if (ftp
->addrtype
== AF_INET6
) {
953 portrange
= IPV6_PORTRANGE_HIGH
;
954 if (setsockopt(s
, IPPROTO_IPV6
, IPV6_PORTRANGE
, (char *)
955 &portrange
, sizeof(portrange
)) < 0) {
962 if (ftp
->addrtype
== AF_INET
) {
963 portrange
= IP_PORTRANGE_HIGH
;
964 if (setsockopt(s
, IPPROTO_IP
, IP_PORTRANGE
, (char *)
965 &portrange
, sizeof(portrange
)) < 0) {
973 getsockname(ftp
->fd_ctrl
, (struct sockaddr
*)&sin
, &i
);
974 sin
.sin4
.sin_port
= 0;
975 i
= ((struct sockaddr
*)&sin
)->sa_len
;
976 if (bind(s
, (struct sockaddr
*)&sin
, i
) < 0) {
981 getsockname(s
,(struct sockaddr
*)&sin
,&i
);
982 if (listen(s
, 1) < 0) {
986 if (sin
.sin4
.sin_family
== AF_INET
) {
988 a
= ntohl(sin
.sin4
.sin_addr
.s_addr
);
989 i
= cmd(ftp
, "PORT %ld,%ld,%ld,%ld,%d,%d",
994 (ntohs(sin
.sin4
.sin_port
) >> 8) & 0xff,
995 ntohs(sin
.sin4
.sin_port
) & 0xff);
996 if (check_code(ftp
, i
, FTP_PORT_HAPPY
)) {
1001 #define UC(b) (((int)b)&0xff)
1003 char hname
[INET6_ADDRSTRLEN
];
1005 sin
.sin6
.sin6_scope_id
= 0;
1006 if (getnameinfo((struct sockaddr
*)&sin
, sin
.sin6
.sin6_len
,
1007 hname
, sizeof(hname
),
1008 NULL
, 0, NI_NUMERICHOST
) != 0) {
1011 i
= cmd(ftp
, "EPRT |%d|%s|%d|", 2, hname
,
1012 htons(sin
.sin6
.sin6_port
));
1013 if (check_code(ftp
, i
, FTP_PORT_HAPPY
)) {
1015 a
= (char *)&sin
.sin6
.sin6_addr
;
1017 "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
1019 UC(a
[0]),UC(a
[1]),UC(a
[2]),UC(a
[3]),
1020 UC(a
[4]),UC(a
[5]),UC(a
[6]),UC(a
[7]),
1021 UC(a
[8]),UC(a
[9]),UC(a
[10]),UC(a
[11]),
1022 UC(a
[12]),UC(a
[13]),UC(a
[14]),UC(a
[15]),
1024 (ntohs(sin
.sin4
.sin_port
) >> 8) & 0xff,
1025 ntohs(sin
.sin4
.sin_port
) & 0xff);
1026 if (check_code(ftp
, i
, FTP_PORT_HAPPY
)) {
1032 if (seekto
&& *seekto
) {
1033 i
= cmd(ftp
, "REST %" PRId64
, *seekto
);
1034 if (i
< 0 || FTP_TIMEOUT(i
)) {
1042 i
= cmd(ftp
, "%s %s", operation
, file
);
1043 if (i
< 0 || i
> 299) {
1048 fd
= accept(s
, 0, 0);
1055 *fp
= fdopen(fd
, mode
);
1064 ai_unmapped(struct addrinfo
*ai
)
1066 struct sockaddr_in6
*sin6
;
1067 struct sockaddr_in sin
;
1069 if (ai
->ai_family
!= AF_INET6
)
1071 if (ai
->ai_addrlen
!= sizeof(struct sockaddr_in6
) ||
1072 sizeof(sin
) > ai
->ai_addrlen
)
1074 sin6
= (struct sockaddr_in6
*)ai
->ai_addr
;
1075 if (!IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
))
1078 memset(&sin
, 0, sizeof(sin
));
1079 sin
.sin_family
= AF_INET
;
1080 sin
.sin_len
= sizeof(struct sockaddr_in
);
1081 memcpy(&sin
.sin_addr
, &sin6
->sin6_addr
.s6_addr
[12],
1082 sizeof(sin
.sin_addr
));
1083 sin
.sin_port
= sin6
->sin6_port
;
1085 ai
->ai_family
= AF_INET
;
1086 memcpy(ai
->ai_addr
, &sin
, sin
.sin_len
);
1087 ai
->ai_addrlen
= sin
.sin_len
;