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