1 /*
2  * Mar  8, 2000 by Hajimu UMEMOTO <ume@mahoroba.org>
3  * $Id: getaddrinfo.c 125 2005-08-27 16:33:10Z ume $
4  *
5  * This module is besed on ssh-1.2.27-IPv6-1.5 written by
6  * KIKUCHI Takahiro <kick@kyoto.wide.ad.jp>
7  */
8 /*
9  * fake library for ssh
10  *
11  * This file includes getaddrinfo(), freeaddrinfo() and gai_strerror().
12  * These funtions are defined in rfc2133.
13  *
14  * But these functions are not implemented correctly. The minimum subset
15  * is implemented for ssh use only. For exapmle, this routine assumes
16  * that ai_family is AF_INET. Don't use it for another purpose.
17  *
18  * In the case not using 'configure --enable-ipv6', this getaddrinfo.c
19  * will be used if you have broken getaddrinfo or no getaddrinfo.
20  */
21 
22 #include <sys/param.h>
23 #include "gai.h"
24 
25 static struct addrinfo *
malloc_ai(int port,u_long addr,int socktype,int proto)26 malloc_ai(int port, u_long addr, int socktype, int proto)
27 {
28     struct addrinfo *ai;
29 
30     ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) +
31 				   sizeof(struct sockaddr_in));
32     if (ai) {
33 	memset(ai, 0, sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
34 	ai->ai_addr = (struct sockaddr *)(ai + 1);
35 	/* XXX -- ssh doesn't use sa_len */
36 	ai->ai_addrlen = sizeof(struct sockaddr_in);
37 #ifdef HAVE_SOCKADDR_SA_LEN
38 	ai->ai_addr->sa_len = sizeof(struct sockaddr_in);
39 #endif
40 	ai->ai_addr->sa_family = ai->ai_family = AF_INET;
41 	((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
42 	((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
43 	ai->ai_socktype = socktype;
44 	ai->ai_protocol = proto;
45 	return ai;
46     } else {
47 	return NULL;
48     }
49 }
50 
51 char *
gai_strerror(int ecode)52 gai_strerror(int ecode)
53 {
54     switch (ecode) {
55     case EAI_MEMORY:
56 	return "memory allocation failure.";
57     case EAI_FAMILY:
58 	return "ai_family not supported.";
59     case EAI_NONAME:
60 	return "hostname nor servname provided, or not known.";
61     case EAI_SERVICE:
62 	return "servname not supported for ai_socktype.";
63     default:
64 	return "unknown error.";
65     }
66 }
67 
68 void
freeaddrinfo(struct addrinfo * ai)69 freeaddrinfo(struct addrinfo *ai)
70 {
71     struct addrinfo *next;
72 
73     if (ai->ai_canonname)
74 	free(ai->ai_canonname);
75     do {
76 	next = ai->ai_next;
77 	free(ai);
78     } while ((ai = next) != NULL);
79 }
80 
81 int
getaddrinfo(const char * hostname,const char * servname,const struct addrinfo * hints,struct addrinfo ** res)82 getaddrinfo(const char *hostname, const char *servname,
83 	    const struct addrinfo *hints, struct addrinfo **res)
84 {
85     struct addrinfo *cur, *prev = NULL;
86     struct hostent *hp;
87     struct in_addr in;
88     int i, port = 0, socktype, proto;
89 
90     if (hints && hints->ai_family != PF_INET && hints->ai_family != PF_UNSPEC)
91 	return EAI_FAMILY;
92 
93     socktype = (hints && hints->ai_socktype) ? hints->ai_socktype
94 					     : SOCK_STREAM;
95     if (hints && hints->ai_protocol)
96 	proto = hints->ai_protocol;
97     else {
98 	switch (socktype) {
99 	case SOCK_DGRAM:
100 	    proto = IPPROTO_UDP;
101 	    break;
102 	case SOCK_STREAM:
103 	    proto = IPPROTO_TCP;
104 	    break;
105 	default:
106 	    proto = 0;
107 	    break;
108 	}
109     }
110     if (servname) {
111 	if (isdigit((int)*servname))
112 	    port = htons(atoi(servname));
113 	else {
114 	    struct servent *se;
115 	    char *pe_proto;
116 
117 	    if (hints && hints->ai_flags & AI_NUMERICSERV)
118 		    return EAI_NONAME;
119 	    switch (socktype) {
120 	    case SOCK_DGRAM:
121 		pe_proto = "udp";
122 		break;
123 	    case SOCK_STREAM:
124 		pe_proto = "tcp";
125 		break;
126 	    default:
127 		pe_proto = NULL;
128 		break;
129 	    }
130 	    if ((se = getservbyname(servname, pe_proto)) == NULL)
131 		return EAI_SERVICE;
132 	    port = se->s_port;
133 	}
134     }
135     if (!hostname) {
136 	if (hints && hints->ai_flags & AI_PASSIVE)
137 	    *res = malloc_ai(port, htonl(0x00000000), socktype, proto);
138 	else
139 	    *res = malloc_ai(port, htonl(0x7f000001), socktype, proto);
140 	if (*res)
141 	    return 0;
142 	else
143 	    return EAI_MEMORY;
144     }
145     if (inet_aton(hostname, &in)) {
146 	*res = malloc_ai(port, in.s_addr, socktype, proto);
147 	if (*res)
148 	    return 0;
149 	else
150 	    return EAI_MEMORY;
151     }
152     if (hints && hints->ai_flags & AI_NUMERICHOST)
153 	return EAI_NONAME;
154     if ((hp = gethostbyname(hostname)) &&
155 	hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
156 	for (i = 0; hp->h_addr_list[i]; i++) {
157 	    if ((cur = malloc_ai(port,
158 				((struct in_addr *)hp->h_addr_list[i])->s_addr,
159 				socktype, proto)) == NULL) {
160 		if (*res)
161 		    freeaddrinfo(*res);
162 		return EAI_MEMORY;
163 	    }
164 	    if (prev)
165 		prev->ai_next = cur;
166 	    else
167 		*res = cur;
168 	    prev = cur;
169 	}
170 	if (hints && hints->ai_flags & AI_CANONNAME && *res) {
171 	    if (((*res)->ai_canonname = strdup(hp->h_name)) == NULL) {
172 		freeaddrinfo(*res);
173 		return EAI_MEMORY;
174 	    }
175 	}
176 	return 0;
177     }
178     return EAI_NONAME;
179 }
180