1 /*
2 * host.c -- DNS lookups of hostnames
3 *
4 * Yet Another FTP Client
5 * Copyright (C) 1998-2001, Martin Hedenfalk <mhe@stacken.kth.se>
6 * Copyright (C) 2012, Sebastian Ramacher <sebastian+dev@ramacher.at>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version. See COPYING for more details.
12 */
13
14 #include "host.h"
15
16 // AI_ADDRCONFIG and AI_V4MAPPED are not defined on OpenBSD
17 #ifndef AI_ADDRCONFIG
18 #define AI_ADDRCONFIG 0
19 #endif
20
21 #ifndef AI_V4MAPPED
22 #define AI_V4MAPPED 0
23 #endif
24
25 struct Host_
26 {
27 char* hostname;
28 int port;
29 int ret;
30
31 struct addrinfo* addr;
32 const struct addrinfo* connected_addr;
33 };
34
host_create(const url_t * urlp)35 Host* host_create(const url_t* urlp)
36 {
37 Host* hostp = xmalloc(sizeof(Host));
38 hostp->hostname = xstrdup(urlp->hostname);
39 hostp->port = urlp->port; /* host byte order */
40 hostp->ret = 0;
41 if(hostp->port <= 0)
42 hostp->port = -1;
43 else
44 hostp->port = htons(hostp->port); /* to network byte order */
45 hostp->addr = NULL;
46 hostp->connected_addr = NULL;
47
48 return hostp;
49 }
50
host_destroy(Host * hostp)51 void host_destroy(Host *hostp)
52 {
53 if (!hostp)
54 return;
55
56 free(hostp->hostname);
57 if (hostp->addr)
58 freeaddrinfo(hostp->addr);
59 free(hostp);
60 }
61
host_lookup(Host * hostp)62 bool host_lookup(Host* hostp)
63 {
64 if (!hostp || !hostp->hostname)
65 return false;
66
67 char* service = NULL;
68 if (hostp->port != -1)
69 {
70 if (asprintf(&service, "%d", ntohs(hostp->port)) == -1)
71 return false;
72 }
73 else
74 service = xstrdup("ftp");
75
76 struct addrinfo hints;
77 memset(&hints, 0, sizeof(hints));
78 #ifdef HAVE_IPV6
79 hints.ai_family = AF_UNSPEC;
80 #else
81 hints.ai_family = AF_INET;
82 #endif
83 hints.ai_socktype = SOCK_STREAM;
84 hints.ai_protocol = IPPROTO_TCP;
85 hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME;
86 #ifdef HAVE_IPV6
87 hints.ai_flags |= AI_V4MAPPED;
88 #endif
89 if (hostp->port != -1)
90 hints.ai_flags |= AI_NUMERICSERV;
91
92 hostp->ret = getaddrinfo(hostp->hostname, service, &hints, &hostp->addr);
93 free(service);
94 if (hostp->ret < 0)
95 {
96 /* Lookup with "ftp" as service. Try again with 21. */
97 if (hostp->port == -1)
98 hostp->ret = getaddrinfo(hostp->hostname, "21", &hints, &hostp->addr);
99
100 if (hostp->ret < 0)
101 return false;
102 }
103
104 if (hostp->port == -1)
105 {
106 if (hostp->addr->ai_family == AF_INET)
107 hostp->port = ((struct sockaddr_in*)hostp->addr->ai_addr)->sin_port;
108 #ifdef HAVE_IPV6
109 else if (hostp->addr->ai_family == AF_INET6)
110 hostp->port = ((struct sockaddr_in6*)hostp->addr->ai_addr)->sin6_port;
111 #endif
112 }
113
114 return true;
115 }
116
117 /* returns port in network byte order */
host_getport(const Host * hostp)118 uint16_t host_getport(const Host *hostp)
119 {
120 return hostp->port;
121 }
122
123 /* returns port in host byte order */
host_gethport(const Host * hostp)124 uint16_t host_gethport(const Host *hostp)
125 {
126 return ntohs(hostp->port);
127 }
128
129 /* returns name as passed to host_set() */
host_getname(const Host * hostp)130 const char *host_getname(const Host *hostp)
131 {
132 if (!hostp)
133 return NULL;
134
135 return hostp->hostname;
136 }
137
host_geterror(const Host * hostp)138 const char* host_geterror(const Host* hostp)
139 {
140 if (!hostp)
141 return NULL;
142
143 return gai_strerror(hostp->ret);
144 }
145
host_getoname(const Host * hostp)146 const char *host_getoname(const Host *hostp)
147 {
148 if (!hostp || !hostp->addr)
149 return NULL;
150
151 return hostp->addr->ai_canonname;
152 }
153
host_getaddrinfo(const Host * hostp)154 const struct addrinfo* host_getaddrinfo(const Host* hostp)
155 {
156 if (!hostp)
157 return NULL;
158
159 return hostp->addr;
160 }
161
host_getip(const Host * hostp)162 char* host_getip(const Host* hostp)
163 {
164 if (!hostp || !hostp->connected_addr)
165 return NULL;
166
167 return printable_address(hostp->connected_addr->ai_addr);
168 }
169
printable_address(const struct sockaddr * sa)170 char* printable_address(const struct sockaddr* sa)
171 {
172 char res[INET6_ADDRSTRLEN];
173 if (sa->sa_family == AF_INET)
174 inet_ntop(sa->sa_family, &((const struct sockaddr_in*)sa)->sin_addr, res, INET6_ADDRSTRLEN);
175 #ifdef HAVE_IPV6
176 else if (sa->sa_family == AF_INET6)
177 inet_ntop(sa->sa_family, &((const struct sockaddr_in6*)sa)->sin6_addr, res, INET6_ADDRSTRLEN);
178 #endif
179 else
180 return NULL;
181
182 return xstrdup(res);
183 }
184
host_connect_addr(Host * hostp,const struct addrinfo * info)185 void host_connect_addr(Host* hostp, const struct addrinfo* info)
186 {
187 if (!hostp)
188 return;
189
190 hostp->connected_addr = info;
191 }
192
193