1 /*
2  * esx_util.c: utility functions for the VMware ESX driver
3  *
4  * Copyright (C) 2010-2012 Red Hat, Inc.
5  * Copyright (C) 2009-2011 Matthias Bolte <matthias.bolte@googlemail.com>
6  * Copyright (C) 2009 Maximilian Wilhelm <max@rfc2324.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library.  If not, see
20  * <http://www.gnu.org/licenses/>.
21  *
22  */
23 
24 #include <config.h>
25 
26 #include "internal.h"
27 #include "datatypes.h"
28 #include "viralloc.h"
29 #include "virlog.h"
30 #include "viruuid.h"
31 #include "vmx.h"
32 #include "esx_private.h"
33 #include "esx_util.h"
34 #include "virstring.h"
35 #include "virsocket.h"
36 
37 #define VIR_FROM_THIS VIR_FROM_ESX
38 
39 VIR_LOG_INIT("esx.esx_util");
40 
41 int
esxUtil_ParseUri(esxUtil_ParsedUri ** parsedUri,virURI * uri)42 esxUtil_ParseUri(esxUtil_ParsedUri **parsedUri, virURI *uri)
43 {
44     int result = -1;
45     size_t i;
46     int noVerify;
47     int autoAnswer;
48     char *tmp;
49 
50     ESX_VI_CHECK_ARG_LIST(parsedUri);
51 
52     *parsedUri = g_new0(esxUtil_ParsedUri, 1);
53 
54     for (i = 0; i < uri->paramsCount; i++) {
55         virURIParam *queryParam = &uri->params[i];
56 
57         if (STRCASEEQ(queryParam->name, "transport")) {
58             g_free((*parsedUri)->transport);
59 
60             (*parsedUri)->transport = g_strdup(queryParam->value);
61 
62             if (STRNEQ((*parsedUri)->transport, "http") &&
63                 STRNEQ((*parsedUri)->transport, "https")) {
64                 virReportError(VIR_ERR_INVALID_ARG,
65                                _("Query parameter 'transport' has unexpected value "
66                                  "'%s' (should be http|https)"),
67                                (*parsedUri)->transport);
68                 goto cleanup;
69             }
70         } else if (STRCASEEQ(queryParam->name, "vcenter")) {
71             g_free((*parsedUri)->vCenter);
72 
73             (*parsedUri)->vCenter = g_strdup(queryParam->value);
74         } else if (STRCASEEQ(queryParam->name, "no_verify")) {
75             if (virStrToLong_i(queryParam->value, NULL, 10, &noVerify) < 0 ||
76                 (noVerify != 0 && noVerify != 1)) {
77                 virReportError(VIR_ERR_INVALID_ARG,
78                                _("Query parameter 'no_verify' has unexpected value "
79                                  "'%s' (should be 0 or 1)"), queryParam->value);
80                 goto cleanup;
81             }
82 
83             (*parsedUri)->noVerify = noVerify != 0;
84         } else if (STRCASEEQ(queryParam->name, "auto_answer")) {
85             if (virStrToLong_i(queryParam->value, NULL, 10, &autoAnswer) < 0 ||
86                 (autoAnswer != 0 && autoAnswer != 1)) {
87                 virReportError(VIR_ERR_INVALID_ARG,
88                                _("Query parameter 'auto_answer' has unexpected "
89                                  "value '%s' (should be 0 or 1)"), queryParam->value);
90                 goto cleanup;
91             }
92 
93             (*parsedUri)->autoAnswer = autoAnswer != 0;
94         } else if (STRCASEEQ(queryParam->name, "proxy")) {
95             /* Expected format: [<type>://]<hostname>[:<port>] */
96             (*parsedUri)->proxy = true;
97             (*parsedUri)->proxy_type = CURLPROXY_HTTP;
98             g_clear_pointer(&(*parsedUri)->proxy_hostname, g_free);
99             (*parsedUri)->proxy_port = 1080;
100 
101             if ((tmp = STRSKIP(queryParam->value, "http://"))) {
102                 (*parsedUri)->proxy_type = CURLPROXY_HTTP;
103             } else if ((tmp = STRSKIP(queryParam->value, "socks://")) ||
104                        (tmp = STRSKIP(queryParam->value, "socks5://"))) {
105                 (*parsedUri)->proxy_type = CURLPROXY_SOCKS5;
106             } else if ((tmp = STRSKIP(queryParam->value, "socks4://"))) {
107                 (*parsedUri)->proxy_type = CURLPROXY_SOCKS4;
108             } else if ((tmp = STRSKIP(queryParam->value, "socks4a://"))) {
109                 (*parsedUri)->proxy_type = CURLPROXY_SOCKS4A;
110             } else if ((tmp = strstr(queryParam->value, "://"))) {
111                 *tmp = '\0';
112 
113                 virReportError(VIR_ERR_INVALID_ARG,
114                                _("Query parameter 'proxy' contains unexpected "
115                                  "type '%s' (should be (http|socks(|4|4a|5))"),
116                                queryParam->value);
117                 goto cleanup;
118             } else {
119                 tmp = queryParam->value;
120             }
121 
122             (*parsedUri)->proxy_hostname = g_strdup(tmp);
123 
124             if ((tmp = strchr((*parsedUri)->proxy_hostname, ':'))) {
125                 if (tmp == (*parsedUri)->proxy_hostname) {
126                     virReportError(VIR_ERR_INVALID_ARG, "%s",
127                                    _("Query parameter 'proxy' doesn't contain a "
128                                      "hostname"));
129                     goto cleanup;
130                 }
131 
132                 *tmp++ = '\0';
133 
134                 if (virStrToLong_i(tmp, NULL, 10,
135                                    &(*parsedUri)->proxy_port) < 0 ||
136                     (*parsedUri)->proxy_port < 1 ||
137                     (*parsedUri)->proxy_port > 65535) {
138                     virReportError(VIR_ERR_INVALID_ARG,
139                                    _("Query parameter 'proxy' has unexpected "
140                                      "port value '%s' (should be [1..65535])"),
141                                    tmp);
142                     goto cleanup;
143                 }
144             }
145         } else {
146             VIR_WARN("Ignoring unexpected query parameter '%s'",
147                      queryParam->name);
148         }
149     }
150 
151     (*parsedUri)->path = g_strdup(uri->path);
152 
153     if (!(*parsedUri)->transport)
154         (*parsedUri)->transport = g_strdup("https");
155 
156     result = 0;
157 
158  cleanup:
159     if (result < 0)
160         esxUtil_FreeParsedUri(parsedUri);
161 
162     return result;
163 }
164 
165 
166 
167 
168 void
esxUtil_FreeParsedUri(esxUtil_ParsedUri ** parsedUri)169 esxUtil_FreeParsedUri(esxUtil_ParsedUri **parsedUri)
170 {
171     if (!parsedUri || !(*parsedUri))
172         return;
173 
174     g_free((*parsedUri)->transport);
175     g_free((*parsedUri)->vCenter);
176     g_free((*parsedUri)->proxy_hostname);
177     g_free((*parsedUri)->path);
178 
179     g_free(*parsedUri);
180 }
181 
182 
183 
184 int
esxUtil_ParseVirtualMachineIDString(const char * id_string,int * id)185 esxUtil_ParseVirtualMachineIDString(const char *id_string, int *id)
186 {
187     /* Try to parse an integer from the complete string. */
188     if (virStrToLong_i(id_string, NULL, 10, id) == 0)
189         return 0;
190 
191     /*
192      * If that fails try to parse an integer from the string tail
193      * assuming the naming scheme Virtual Center seems to use.
194      */
195     if (STRPREFIX(id_string, "vm-")) {
196         if (virStrToLong_i(id_string + 3, NULL, 10, id) == 0)
197             return 0;
198     }
199 
200     return -1;
201 }
202 
203 
204 
205 int
esxUtil_ParseDatastorePath(const char * datastorePath,char ** datastoreName,char ** directoryName,char ** directoryAndFileName)206 esxUtil_ParseDatastorePath(const char *datastorePath, char **datastoreName,
207                            char **directoryName, char **directoryAndFileName)
208 {
209     int result = -1;
210     g_autofree char *copyOfDatastorePath = NULL;
211     char *tmp = NULL;
212     char *saveptr = NULL;
213     char *preliminaryDatastoreName = NULL;
214     char *preliminaryDirectoryAndFileName = NULL;
215 
216     if ((datastoreName && *datastoreName) ||
217         (directoryName && *directoryName) ||
218         (directoryAndFileName && *directoryAndFileName)) {
219         virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
220         return -1;
221     }
222 
223     copyOfDatastorePath = g_strdup(datastorePath);
224 
225     /* Expected format: '[<datastore>] <path>' where <path> is optional */
226     if (!(tmp = STRSKIP(copyOfDatastorePath, "[")) || *tmp == ']' ||
227         !(preliminaryDatastoreName = strtok_r(tmp, "]", &saveptr))) {
228         virReportError(VIR_ERR_INVALID_ARG,
229                        _("Datastore path '%s' doesn't have expected format "
230                          "'[<datastore>] <path>'"), datastorePath);
231         goto cleanup;
232     }
233 
234     if (datastoreName)
235         *datastoreName = g_strdup(preliminaryDatastoreName);
236 
237     preliminaryDirectoryAndFileName = strtok_r(NULL, "", &saveptr);
238 
239     if (!preliminaryDirectoryAndFileName) {
240         preliminaryDirectoryAndFileName = (char *)"";
241     } else {
242         preliminaryDirectoryAndFileName +=
243           strspn(preliminaryDirectoryAndFileName, " ");
244     }
245 
246     if (directoryAndFileName)
247         *directoryAndFileName = g_strdup(preliminaryDirectoryAndFileName);
248 
249     if (directoryName) {
250         /* Split <path> into <directory>/<file> and remove /<file> */
251         tmp = strrchr(preliminaryDirectoryAndFileName, '/');
252 
253         if (tmp)
254             *tmp = '\0';
255 
256         *directoryName = g_strdup(preliminaryDirectoryAndFileName);
257     }
258 
259     result = 0;
260 
261  cleanup:
262     if (result < 0) {
263         if (datastoreName)
264             g_clear_pointer(datastoreName, g_free);
265 
266         if (directoryName)
267             g_clear_pointer(directoryName, g_free);
268 
269         if (directoryAndFileName)
270             g_clear_pointer(directoryAndFileName, g_free);
271     }
272 
273     return result;
274 }
275 
276 
277 
278 int
esxUtil_ResolveHostname(const char * hostname,char ** ipAddress)279 esxUtil_ResolveHostname(const char *hostname, char **ipAddress)
280 {
281     struct addrinfo hints;
282     struct addrinfo *result = NULL;
283     int errcode;
284     g_autofree char *address = NULL;
285 
286     memset(&hints, 0, sizeof(hints));
287 
288     hints.ai_flags = AI_ADDRCONFIG;
289     hints.ai_family = AF_INET;
290     hints.ai_socktype = SOCK_STREAM;
291     hints.ai_protocol = 0;
292 
293     errcode = getaddrinfo(hostname, NULL, &hints, &result);
294 
295     if (errcode != 0) {
296         virReportError(VIR_ERR_INTERNAL_ERROR,
297                        _("IP address lookup for host '%s' failed: %s"), hostname,
298                        gai_strerror(errcode));
299         return -1;
300     }
301 
302     if (!result) {
303         virReportError(VIR_ERR_INTERNAL_ERROR,
304                        _("No IP address for host '%s' found: %s"), hostname,
305                        gai_strerror(errcode));
306         return -1;
307     }
308 
309     address = g_new0(char, NI_MAXHOST);
310     errcode = getnameinfo(result->ai_addr, result->ai_addrlen, address,
311                           NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
312     freeaddrinfo(result);
313 
314     if (errcode != 0) {
315         virReportError(VIR_ERR_INTERNAL_ERROR,
316                        _("Formatting IP address for host '%s' failed: %s"), hostname,
317                        gai_strerror(errcode));
318         return -1;
319     }
320 
321     *ipAddress = g_strdup(address);
322 
323     return 0;
324 }
325 
326 
327 
328 int
esxUtil_ReformatUuid(const char * input,char * output)329 esxUtil_ReformatUuid(const char *input, char *output)
330 {
331     unsigned char uuid[VIR_UUID_BUFLEN];
332 
333     if (virUUIDParse(input, uuid) < 0) {
334         virReportError(VIR_ERR_INTERNAL_ERROR,
335                        _("Could not parse UUID from string '%s'"),
336                        input);
337         return -1;
338     }
339 
340     virUUIDFormat(uuid, output);
341 
342     return 0;
343 }
344 
345 
346 
347 char *
esxUtil_EscapeBase64(const char * string)348 esxUtil_EscapeBase64(const char *string)
349 {
350     /* 'normal' characters don't get base64 encoded */
351     static const char *normal =
352       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'(),. _-";
353 
354     /* VMware uses ',' instead of the path separator '/' in the base64 alphabet */
355     static const char *base64 =
356       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
357 
358     g_auto(virBuffer) buffer = VIR_BUFFER_INITIALIZER;
359     const char *tmp1 = string;
360     size_t length;
361     unsigned char c1, c2, c3;
362 
363     /* Escape sequences of non-'normal' characters as base64 without padding */
364     while (*tmp1 != '\0') {
365         length = strspn(tmp1, normal);
366 
367         if (length > 0) {
368             virBufferAdd(&buffer, tmp1, length);
369 
370             tmp1 += length;
371         } else {
372             length = strcspn(tmp1, normal);
373 
374             virBufferAddChar(&buffer, '+');
375 
376             while (length > 0) {
377                 c1 = *tmp1++;
378                 c2 = length > 1 ? *tmp1++ : 0;
379                 c3 = length > 2 ? *tmp1++ : 0;
380 
381                 virBufferAddChar(&buffer, base64[(c1 >> 2) & 0x3f]);
382                 virBufferAddChar(&buffer, base64[((c1 << 4) + (c2 >> 4)) & 0x3f]);
383 
384                 if (length > 1)
385                     virBufferAddChar(&buffer, base64[((c2 << 2) + (c3 >> 6)) & 0x3f]);
386 
387                 if (length > 2)
388                     virBufferAddChar(&buffer, base64[c3 & 0x3f]);
389 
390                 length -= length > 3 ? 3 : length;
391             }
392 
393             if (*tmp1 != '\0')
394                 virBufferAddChar(&buffer, '-');
395         }
396     }
397 
398     return virBufferContentAndReset(&buffer);
399 }
400 
401 
402 
403 void
esxUtil_ReplaceSpecialWindowsPathChars(char * string)404 esxUtil_ReplaceSpecialWindowsPathChars(char *string)
405 {
406     /* '/' and '\\' are missing on purpose */
407     static const char *specials = "\"*<>:|?";
408 
409     char *tmp = string;
410     size_t length;
411 
412     while (*tmp != '\0') {
413         length = strspn(tmp, specials);
414 
415         while (length > 0) {
416             *tmp++ = '_';
417             --length;
418         }
419 
420         if (*tmp != '\0')
421             ++tmp;
422     }
423 }
424 
425 
426 
427 char *
esxUtil_EscapeDatastoreItem(const char * string)428 esxUtil_EscapeDatastoreItem(const char *string)
429 {
430     g_autofree char *replaced = NULL;
431     g_autofree char *escaped1 = NULL;
432 
433     replaced = g_strdup(string);
434 
435     esxUtil_ReplaceSpecialWindowsPathChars(replaced);
436 
437     escaped1 = virVMXEscapeHexPercent(replaced);
438 
439     if (!escaped1)
440         return NULL;
441 
442     return esxUtil_EscapeBase64(escaped1);
443 }
444 
445 
446 
447 char *
esxUtil_EscapeForXml(const char * string)448 esxUtil_EscapeForXml(const char *string)
449 {
450     g_auto(virBuffer) buffer = VIR_BUFFER_INITIALIZER;
451 
452     virBufferEscapeString(&buffer, "%s", string);
453 
454     return virBufferContentAndReset(&buffer);
455 }
456