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