1 /*
2   Copyright 2021 Northern.tech AS
3 
4   This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; version 3.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
18 
19   To the extent this program is licensed as part of the Enterprise
20   versions of CFEngine, the applicable Commercial Open Source License
21   (COSL) may apply to this file if you as a licensee so wish it. See
22   included file COSL.txt.
23 */
24 
25 
26 #include <platform.h>
27 #include <communication.h>
28 
29 #include <connection_info.h>
30 #include <stat_cache.h>                                 /* Stat */
31 #include <alloc.h>                                      /* xmalloc,... */
32 #include <logging.h>                                    /* Log */
33 #include <misc_lib.h>                                   /* ProgrammingError */
34 #include <buffer.h>                                     /* Buffer */
35 #include <ip_address.h>                                 /* IPAddress */
36 
37 
NewAgentConn(const char * server,const char * port,ConnectionFlags flags)38 AgentConnection *NewAgentConn(const char *server, const char *port,
39                               ConnectionFlags flags)
40 {
41     AgentConnection *conn = xcalloc(1, sizeof(AgentConnection));
42     conn->conn_info = ConnectionInfoNew();
43     conn->this_server = xstrdup(server);
44     conn->this_port = (port == NULL) ? NULL : xstrdup(port);
45     conn->flags = flags;
46     conn->encryption_type = 'c';
47     conn->authenticated = false;
48     return conn;
49 }
50 
DeleteAgentConn(AgentConnection * conn)51 void DeleteAgentConn(AgentConnection *conn)
52 {
53     Stat *sp = conn->cache;
54 
55     while (sp != NULL)
56     {
57         Stat *previous = sp;
58         sp = sp->next;
59         DestroyStatCache(previous);
60     }
61 
62     ConnectionInfoDestroy(&conn->conn_info);
63     free(conn->this_server);
64     free(conn->this_port);
65     free(conn->session_key);
66     *conn = (AgentConnection) {0};
67     free(conn);
68 }
69 
IsIPV6Address(char * name)70 bool IsIPV6Address(char *name)
71 {
72     if (!name)
73     {
74         return false;
75     }
76     Buffer *buffer = BufferNewFrom(name, strlen(name));
77     if (!buffer)
78     {
79         return false;
80     }
81     IPAddress *ip_address = NULL;
82     bool is_ip = false;
83     is_ip = IPAddressIsIPAddress(buffer, &ip_address);
84     if (!is_ip)
85     {
86         BufferDestroy(buffer);
87         return false;
88     }
89     if (IPAddressType(ip_address) != IP_ADDRESS_TYPE_IPV6)
90     {
91         BufferDestroy(buffer);
92         IPAddressDestroy(&ip_address);
93         return false;
94     }
95     BufferDestroy(buffer);
96     IPAddressDestroy(&ip_address);
97     return true;
98 }
99 
100 /*******************************************************************/
101 
IsIPV4Address(char * name)102 bool IsIPV4Address(char *name)
103 {
104     if (!name)
105     {
106         return false;
107     }
108     Buffer *buffer = BufferNewFrom(name, strlen(name));
109     if (!buffer)
110     {
111         return false;
112     }
113     IPAddress *ip_address = NULL;
114     bool is_ip = false;
115     is_ip = IPAddressIsIPAddress(buffer, &ip_address);
116     if (!is_ip)
117     {
118         BufferDestroy(buffer);
119         return false;
120     }
121     if (IPAddressType(ip_address) != IP_ADDRESS_TYPE_IPV4)
122     {
123         BufferDestroy(buffer);
124         IPAddressDestroy(&ip_address);
125         return false;
126     }
127     BufferDestroy(buffer);
128     IPAddressDestroy(&ip_address);
129     return true;
130 }
131 
132 /*****************************************************************************/
133 
134 /**
135  * @brief DNS lookup of hostname, store the address as string into dst of size
136  * dst_size.
137  * @return -1 in case of unresolvable hostname or other error.
138  */
Hostname2IPString(char * dst,const char * hostname,size_t dst_size)139 int Hostname2IPString(char *dst, const char *hostname, size_t dst_size)
140 {
141     int ret;
142     struct addrinfo *response = NULL, *ap;
143     struct addrinfo query = {
144         .ai_family = AF_UNSPEC,
145         .ai_socktype = SOCK_STREAM
146     };
147 
148     if (dst_size < CF_MAX_IP_LEN)
149     {
150         ProgrammingError("Hostname2IPString got %zu, needs at least"
151                          " %d length buffer for IPv6 portability!",
152                          dst_size, CF_MAX_IP_LEN);
153     }
154 
155     ret = getaddrinfo(hostname, NULL, &query, &response);
156     if (ret != 0)
157     {
158         Log(LOG_LEVEL_INFO,
159             "Unable to lookup hostname '%s' or cfengine service. (getaddrinfo: %s)",
160             hostname, gai_strerror(ret));
161         if (response != NULL)
162         {
163             freeaddrinfo(response);
164         }
165         return -1;
166     }
167 
168     for (ap = response; ap != NULL; ap = ap->ai_next)
169     {
170         /* No lookup, just convert numeric IP to string. */
171         int ret2 = getnameinfo(ap->ai_addr, ap->ai_addrlen,
172                                dst, dst_size, NULL, 0, NI_NUMERICHOST);
173         if (ret2 == 0)
174         {
175             freeaddrinfo(response);
176             return 0;                                           /* Success */
177         }
178     }
179 
180     assert(response != NULL);               /* getaddrinfo() was successful */
181     freeaddrinfo(response);
182 
183     Log(LOG_LEVEL_ERR,
184         "Hostname2IPString: ERROR even though getaddrinfo returned success!");
185     return -1;
186 }
187 
188 /*****************************************************************************/
189 
190 /**
191  * @brief Reverse DNS lookup of ipaddr, store the address as string into dst
192  * of size dst_size.
193  * @return -1 in case of unresolvable IP address or other error.
194  */
IPString2Hostname(char * dst,const char * ipaddr,size_t dst_size)195 int IPString2Hostname(char *dst, const char *ipaddr, size_t dst_size)
196 {
197     int ret;
198     struct addrinfo *response = NULL;
199 
200     /* First convert ipaddr string to struct sockaddr, with no DNS query. */
201     struct addrinfo query = {
202         .ai_flags = AI_NUMERICHOST
203     };
204 
205     ret = getaddrinfo(ipaddr, NULL, &query, &response);
206     if (ret != 0)
207     {
208         Log(LOG_LEVEL_ERR,
209             "Unable to convert IP address '%s'. (getaddrinfo: %s)",
210             ipaddr, gai_strerror(ret));
211         if (response != NULL)
212         {
213             freeaddrinfo(response);
214         }
215         return -1;
216     }
217 
218     /* response should only have one reply, so no need to iterate over the
219      * response struct addrinfo. */
220 
221     /* Reverse DNS lookup. NI_NAMEREQD forces an error if not resolvable. */
222     ret = getnameinfo(response->ai_addr, response->ai_addrlen,
223                       dst, dst_size, NULL, 0, NI_NAMEREQD);
224     if (ret != 0)
225     {
226         Log(LOG_LEVEL_INFO,
227             "Couldn't reverse resolve '%s'. (getaddrinfo: %s)",
228             ipaddr, gai_strerror(ret));
229         freeaddrinfo(response);
230         return -1;
231     }
232 
233     assert(response != NULL);               /* getaddrinfo() was successful */
234     freeaddrinfo(response);
235     return 0;                                                   /* Success */
236 }
237 
238 /*****************************************************************************/
239 
SocketFamily(int sd)240 unsigned short SocketFamily(int sd)
241 {
242     struct sockaddr_storage ss = {0};
243     socklen_t len = sizeof(ss);
244 
245     if (getsockname(sd, (struct sockaddr *) &ss, &len) == -1)
246     {
247         Log(LOG_LEVEL_ERR, "Could not get socket family. (getsockname: %s)", GetErrorStr());
248     }
249 
250     return ss.ss_family;
251 }
252