int _qvd_create_dir(qvdclient *qvd, const char *home, const char *subdir) { char path[MAX_PATH_STRING]; struct stat fs_stat; int result; snprintf(path, MAX_PATH_STRING - 1, "%s/%s", home, subdir); path[MAX_PATH_STRING - 1] = '\0'; result = stat(path, &fs_stat); if (result == -1) { if (errno != ENOENT) { qvd_error(qvd, "Error accessing directory $HOME/%s (%s), with error: %s\n", subdir, path, strerror(errno)); return 0; } result = mkdir(path, 0755); if (result) { qvd_error(qvd, "Error creating directory $HOME/%s (%s), with error: %s\n", subdir, path, strerror(errno)); return 0; } return 1; } if (!S_ISDIR(fs_stat.st_mode)) { qvd_error(qvd, "Error accessing dir $HOME/%s (%s) the file is not a directory", subdir, path); return 0; } return 1; }
void qvd_set_cert_files(qvdclient *qvd, const char *client_cert, const char *client_key) { if (client_cert == NULL || client_key == NULL) { qvd_printf("Disabling client certificate\n"); qvd->use_client_cert = 0; return; } if (access(client_cert, R_OK) != 0) { qvd_error(qvd, "Cert file %s is not accessible: %s\n", client_cert, strerror(errno)); qvd->use_client_cert = 0; return; } if (access(client_key, R_OK) != 0) { qvd_error(qvd, "Key file %s is not accessible: %s\n", client_key, strerror(errno)); qvd->use_client_cert = 0; return; } strncpy(qvd->client_cert, client_cert, MAX_PATH_STRING); qvd->client_cert[MAX_PATH_STRING - 1] = '\0'; strncpy(qvd->client_key, client_key, MAX_PATH_STRING); qvd->client_key[MAX_PATH_STRING - 1] = '\0'; qvd->use_client_cert = 1; qvd_printf("Setting client_cert to <%s> and client_key to <%s> and enabling client certificate send", qvd->client_cert, qvd->client_key); return; }
/* * Sets callback to call the java method print_progress from the object * progressHandler of the class QvdClientWrapper */ int progress_callback(qvdclient *qvd, const char *message) { /* TODO rename callbackhandler_environment_struct and reuse it for this case */ jstring message_str; struct callbackhandler_environment_struct *callbackhandler_env; JNIEnv *env; jclass temp; qvd_printf("progress_callback\n"); callbackhandler_env = (struct callbackhandler_environment_struct *) qvd->userdata; qvd_printf("progress_callback:progressHandler: %p, jvm: %p\n", callbackhandler_env->progressCallbackHandler, callbackhandler_env->jvm); (*(callbackhandler_env->jvm))->AttachCurrentThread(callbackhandler_env->jvm, (void **)&env, NULL); qvd_printf("progress_callback:progressHandler: %p, jvm: %p, env: %p\n", callbackhandler_env->progressCallbackHandler, callbackhandler_env->jvm, env); if (env == NULL) { qvd_error(qvd, "Error obtaining JNIEnv * from jvm\n"); return 0; } /* Might be null if object is not defined */ if (!callbackhandler_env->progressCallbackHandler) { qvd_printf("progressCallbackHandler object is null, returning 0\n"); return 0; } qvd_printf("progressCallbackHandler is non null\n"); message_str = (*env)->NewStringUTF(env, message); if (message_str == NULL) { qvd_error(qvd, "Error allocating memory for message_str\n"); return 0; } qvd_printf("message_str is non null"); temp = (*env)->GetObjectClass(env, callbackhandler_env->progressCallbackHandler); qvdprogresshandler_cls = (*env)->NewGlobalRef(env, temp); (*env)->DeleteLocalRef(env, temp); if (qvdprogresshandler_cls == NULL) { qvd_error(qvd, "Error finding class for QvdProgressHandler interface"); return 0; } print_progress_mid = (*env)->GetMethodID(env, qvdprogresshandler_cls, "print_progress", "(Ljava/lang/String;)V"); if (print_progress_mid == NULL) { qvd_error(qvd, "Error finding method for interface QvdProgressHandler print_progress\n"); return 0; } (*env)->CallVoidMethod(env, callbackhandler_env->progressCallbackHandler, print_progress_mid, message_str); qvd_printf("After CallVoidMethod\n"); (*env)->DeleteLocalRef(env, message_str); return 1; }
int qvd_connect_to_vm(qvdclient *qvd, int id) { int result, proxyFd, fd; long curlsock; qvd_printf("qvd_connect_to_vm(%p,%d)", qvd, id); if (qvd->display && (*(qvd->display)) != '\0') { qvd_printf("Setting DISPLAY to %s\n", qvd->display); if (setenv(DISPLAY_ENV, qvd->display, 1)) { qvd_error(qvd, "Error setting DISPLAY to %s. errno: %d (%s)", qvd->display, errno, strerror(errno)); } } if (qvd->home && (*(qvd->home)) != '\0') { qvd_printf("Setting NX_HOME to %s\n", qvd->home); if (setenv("NX_HOME", qvd->home, 1)) { qvd_error(qvd, "Error setting NX_HOME to %s. errno: %d (%s)", qvd->home, errno, strerror(errno)); } } if (!_qvd_set_certdir(qvd)) { qvd_printf("Please set the cert dir"); return 5; } qvd->end_connection = 0; result = _qvd_switch_protocols(qvd, id); _qvd_print_environ(); /* if non zero return with error */ if (result) return result; curl_easy_getinfo(qvd->curl, CURLINFO_LASTSOCKET, &curlsock); fd = (int) curlsock; if ((proxyFd = _qvd_proxy_connect(qvd)) < 0) return 4; qvd_printf("Remote fd: %d Local fd: %d\n", fd, proxyFd); qvd_printf("Before _qvd_client_loop\n"); result = _qvd_client_loop(qvd, fd, proxyFd); qvd_progress(qvd, "End of QVD connection"); shutdown(proxyFd, 2); qvd_printf("before NXTransDestroy\n"); NXTransDestroy(NX_FD_ANY); qvd_printf("after NXTransDestroy\n"); if (result) return 6; return 0; }
/* * Internal funcs for qvd_connect_to_vm */ int _qvd_proxy_connect(qvdclient *qvd) { int proxyPair[2]; if (socketpair(PF_UNIX, SOCK_STREAM, 0, proxyPair) < 0) { qvd_error(qvd, "Error creating proxy socket <%s>\n", strerror(errno)); return -1; } /* if (NXTransCreate(proxyPair[0], NX_MODE_SERVER, "nx/nx,data=0,delta=0,cache=16384,pack=0:0") < 0)*/ if (NXTransCreate(proxyPair[0], NX_MODE_SERVER, qvd->nx_options) < 0) { qvd_error(qvd, "Error creating proxy transport <%s>\n", strerror(errno)); return -1; } return proxyPair[1]; }
int _qvd_set_certdir(qvdclient *qvd) { char *home = getenv(HOME_ENV); char *appdata = getenv(APPDATA_ENV); int result; if (home == NULL && appdata == NULL && !qvd->home && (*(qvd->home)) == '\0' && !_qvd_dir_exists(qvd, qvd->home) && _qvd_dir_exists(qvd, home) && !_qvd_dir_exists(qvd, appdata)) { qvd_error(qvd, "Error %s and %s environment var were not defined, cannot save to $HOME/.qvd/certs, you can try to set also qvd_set_home", HOME_ENV, APPDATA_ENV); return 0; } if (qvd->home && (*(qvd->home)) && _qvd_dir_exists(qvd, qvd->home)) { home = qvd->home; } else if (home != NULL && _qvd_dir_exists(qvd, home)) { qvd_set_home(qvd, home); qvd_printf("using %s environment var", HOME_ENV); } else if (appdata != NULL && _qvd_dir_exists(qvd, appdata)) { qvd_set_home(qvd, appdata); home = appdata; qvd_printf("%s was not defined using %s environment var", HOME_ENV, APPDATA_ENV); } /* Define .qvd/certs in qvdclient.h */ if (!_qvd_create_dir(qvd, home, CONF_DIR)) return 0; if (!_qvd_create_dir(qvd, home, CERT_DIR)) return 0; snprintf(qvd->certpath, MAX_PATH_STRING, "%s/%s", home, CERT_DIR); qvd->certpath[MAX_PATH_STRING] = '\0'; if (strlen(qvd->certpath) == MAX_PATH_STRING) { qvd_error(qvd, "Cert string too long (%d) recompile program. Path is %s", MAX_PATH_STRING, qvd->certpath); return 0; } qvd_printf("Setting cert path to %s\n", qvd->certpath); curl_easy_setopt(qvd->curl, CURLOPT_CAPATH, qvd->certpath); return 1; }
int _set_display_if_not_set(qvdclient *qvd) { char *display = getenv(DISPLAY_ENV); if (display == NULL || *display == '\0') { qvd_error(qvd, "The display variable was not set, setting %s to %s", DISPLAY_ENV, DEFAULT_DISPLAY); setenv(DISPLAY_ENV, DEFAULT_DISPLAY, 1); return 1; } return 0; }
int _qvd_save_certificate(qvdclient *qvd, X509 *cert, int depth, BUF_MEM *biomem) { char path[MAX_PATH_STRING]; int fd, result; snprintf(path, MAX_PATH_STRING - 1, "%s/%lx.%d", qvd->certpath, X509_subject_name_hash(cert), depth); path[MAX_PATH_STRING - 1] = '\0'; if (strlen(path) == MAX_PATH_STRING) { qvd_error(qvd, "Cert string too long (%d) recompile program. Path is %s", MAX_PATH_STRING, path); return 0; } fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, 0644); if (fd == -1) { qvd_error(qvd, "Error creating file %s: %s", path, strerror(errno)); return 0; } result = write(fd, biomem->data, strlen(biomem->data)); if (result == -1) { qvd_error(qvd, "Error writing file %s: %s", path, strerror(errno)); return 0; } if (result != strlen(biomem->data)) { qvd_error(qvd, "Error writing file not enough bytes written in %s: %d vs %d", path, result, strlen(biomem->data)); return 0; } result = close(fd); if (result == -1) { qvd_error(qvd, "Error closing file %s: %s", path, strerror(errno)); return 0; } qvd_printf("Successfully saved cert in %s\n", path); return 1; }
int qvd_stop_vm(qvdclient *qvd, int vm) { char url[MAX_BASEURL]; int i; long http_code = 0; json_error_t error; char *command = "/qvd/stop_vm"; if (!_qvd_set_certdir(qvd)) { qvd_printf("Please set the cert dir"); return 1; } if (snprintf(url, MAX_BASEURL, "%s%s", qvd->baseurl, command) >= MAX_BASEURL) { qvd_error(qvd, "Error initializing url in list_of_vm, length is longer than %d\n", MAX_BASEURL); return 2; } _qvd_use_client_cert(qvd); curl_easy_setopt(qvd->curl, CURLOPT_URL, url); /* curl_easy_setopt(curl, CURLOPT_WRITEDATA, &jsonBuffer); */ qvd->res = curl_easy_perform(qvd->curl); qvd_printf("After easy_perform: %ul\n", qvd->res); if (qvd->res) { qvd_printf("Error accessing url: <%s>, error code: %ul\n", url, qvd->res); qvd_error(qvd, "Error accessing list of VMs: %s\n", curl_easy_strerror(qvd->res)); return 3; } curl_easy_getinfo (qvd->curl, CURLINFO_RESPONSE_CODE, &http_code); if (http_code == 401) { qvd_error(qvd, "Error authenticating user\n"); return 4; } qvd_printf("No error and no auth error after curl_easy_perform\n"); /* QvdBufferInit(&(qvd->buffer)); */ return 0; }
int _qvd_dir_exists(qvdclient *qvd, const char *path) { struct stat fs_stat; int result; result = stat(path, &fs_stat); if (!S_ISDIR(fs_stat.st_mode)) { qvd_error(qvd, "Error accessing dir %s the file is not a directory\n", path); return 0; } return 1; }
int _qvd_set_base64_auth(qvdclient *qvd) { CURLcode error; char *ptr = NULL, *content; size_t outlen; int result = 0; char *digest = NULL; BIO *bio, *b64; // Digest using the openssl BIO functionality b64 = BIO_new(BIO_f_base64()); bio = BIO_new(BIO_s_mem()); bio = BIO_push(b64, bio); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO_write(b64, qvd->userpwd, strlen(qvd->userpwd)); BIO_flush(b64); outlen = BIO_get_mem_data(bio, &ptr); if ( outlen >= MAX_AUTHDIGEST-1 ) { qvd_error(qvd, "The authdigest string for %s is longer than %d\n", qvd->userpwd, MAX_AUTHDIGEST); result = 1; } else { // The resulting digest isn't zero terminated memcpy(qvd->authdigest, ptr, outlen); qvd->authdigest[outlen] = '\0'; #ifdef TRACE qvd_printf("The conversion to base64 from <%s> is <%s>", qvd->userpwd, qvd->authdigest); #endif result = 0; } BIO_free_all(bio); /* hack for base64 encode of "[email protected]:O3xTMCQ3" */ /* snprintf(qvd->authdigest, MAX_AUTHDIGEST, "%s", "bml0b0BkZWlyby5jb206TzN4VE1DUTM=");*/ return result; }
/* * Sets callback to call the java method certificate_verification from the object * certificateHandler of the class QvdClientWrapper * It gets from the qvd->userdata which points to a struct of callbackhandler_environment_struct * A pointer to the jvm and one to the QvdclientWrapper->unknowncertCallbackHandler object * From there we get the class of the certifcateHandler and the * method certificate_verification, which we invoke, and return the result. */ int accept_unknown_cert_callback(qvdclient *qvd, const char *cert_pem_str, const char *cert_pem_data) { jstring jcert_pem_str, jcert_pem_data; jboolean response; struct callbackhandler_environment_struct *callbackhandler_env; JNIEnv *env; jclass temp; qvd_printf("accept_unknown_cert_callback\n"); callbackhandler_env = (struct callbackhandler_environment_struct *) qvd->userdata; qvd_printf("accept_unknown_cert_callback:certificateHandler: %p, jvm: %p\n", callbackhandler_env->unknowncertCallbackHandler, callbackhandler_env->jvm); (*(callbackhandler_env->jvm))->AttachCurrentThread(callbackhandler_env->jvm, (void **)&env, NULL); qvd_printf("accept_unknown_cert_callback:certificateHandler: %p, jvm: %p, env: %p\n", callbackhandler_env->unknowncertCallbackHandler, callbackhandler_env->jvm, env); if (env == NULL) { qvd_error(qvd, "Error obtaining JNIEnv * from jvm\n"); return 0; } /* Might be null if object is not defined */ if (!callbackhandler_env->unknowncertCallbackHandler) { qvd_printf("certificateHandler object is null, returning false (certificate rejected)\n"); return 0; } qvd_printf("certhandler is non null\n"); jcert_pem_str = (*env)->NewStringUTF(env, cert_pem_str); if (jcert_pem_str == NULL) { qvd_error(qvd, "Error allocating memory for jcert_pem_str\n"); return 0; } qvd_printf("jcert_pem_str is non null\n"); jcert_pem_data = (*env)->NewStringUTF(env, cert_pem_data); if (jcert_pem_data == NULL) { qvd_error(qvd, "Error allocating memory for jcert_pem_data\n"); return 0; } qvd_printf("jcert_pem_data is non null\n"); temp = (*env)->GetObjectClass(env, callbackhandler_env->unknowncertCallbackHandler); qvdunknowncerthandler_cls = (*env)->NewGlobalRef(env, temp); (*env)->DeleteLocalRef(env, temp); if (qvdunknowncerthandler_cls == NULL) { qvd_error(qvd, "Error finding class for QvdUnknownCertificateHandler interface"); return -1; } certificate_verification_mid = (*env)->GetMethodID(env, qvdunknowncerthandler_cls, "certificate_verification", "(Ljava/lang/String;Ljava/lang/String;)Z"); if (certificate_verification_mid == NULL) { qvd_error(qvd, "Error finding method for interface QvdUnknownCertificateHandler certicate_verification\n"); return 0; } response = (*env)->CallBooleanMethod(env, callbackhandler_env->unknowncertCallbackHandler, certificate_verification_mid, jcert_pem_str, jcert_pem_data); qvd_printf("After CallBooleanMethod\n"); qvd_printf("After CallBooleanMethod response, %d\n", response); (*env)->DeleteLocalRef(env, jcert_pem_str); (*env)->DeleteLocalRef(env, jcert_pem_data); return response; }
int _qvd_switch_protocols(qvdclient *qvd, int id) { fd_set myset, zero; size_t bytes_sent, bytes_received, bytes_received_total; int socket, i, content_length, content_size_parsed; char url[MAX_BASEURL]; char base64auth[MAX_PARAM]; char *ptr, *content; _qvd_use_client_cert(qvd); curl_easy_setopt(qvd->curl, CURLOPT_URL, qvd->baseurl); curl_easy_setopt(qvd->curl, CURLOPT_CONNECT_ONLY, 1L); curl_easy_perform(qvd->curl); curl_easy_getinfo(qvd->curl, CURLINFO_LASTSOCKET, &socket); /* if (snprintf(url, MAX_BASEURL, "GET /qvd/connect_to_vm?id=%d&qvd.client.os=%s&qvd.client.fullscreen=%d&qvd.client.geometry=%s&qvd.client.link=%s&qvd.client.keyboard=%s&qvd.client.printing.enabled=%d HTTP/1.1\nAuthorization: Basic %s\nConnection: Upgrade\nUpgrade: QVD/1.0\n\n", id, qvd->os, qvd->fullscreen, qvd->geometry, qvd->link, qvd->keyboard, qvd->print_enabled, qvd->authdigest) >= MAX_BASEURL) { */ if (snprintf(url, MAX_BASEURL, "GET /qvd/connect_to_vm?id=%d&qvd.client.os=%s&qvd.client.geometry=%s&qvd.client.link=%s&qvd.client.keyboard=%s&qvd.client.fullscreen=%d HTTP/1.1\nAuthorization: Basic %s\nConnection: Upgrade\nUpgrade: QVD/1.0\n\n", id, qvd->os, qvd->geometry, qvd->link, qvd->keyboard, qvd->fullscreen, qvd->authdigest) >= MAX_BASEURL) { qvd_error(qvd, "Error initializing authdigest\n"); return 1; } qvd_printf("Switch protocols the url is: <%s>\n", url); /* char *url = "GET /qvd/connect_to_vm?id=1&qvd.client.os=linux&qvd.client.fullscreen=&qvd.client.geometry=800x600&qvd.client.link=local&qvd.client.keyboard=pc105%2Fus&qvd.client.printing.enabled=0 HTTP/1.1\nAuthorization: Basic bml0bzpuaXRv\nConnection: Upgrade\nUpgrade: QVD/1.0\n\n"; */ if ((qvd->res = curl_easy_send(qvd->curl, url, strlen(url) , &bytes_sent )) != CURLE_OK ) { qvd_error(qvd, "An error ocurred in first curl_easy_send: %ul <%s>\n", qvd->res, curl_easy_strerror(qvd->res)); return 1; } /* TODO perhaps put this in another func ??? */ FD_ZERO(&myset); FD_ZERO(&zero); FD_SET(socket, &myset); qvd_printf("Before select on send socket is: %d\n", socket); for (i=0; i<MAX_HTTP_RESPONSES_FOR_UPGRADE; ++i) { /* TODO define timeouts perhaps in qvd_init */ select(socket+1, &myset, &zero, &zero, NULL); if ((qvd->res = curl_easy_recv(qvd->curl, qvd->buffer.data, BUFFER_SIZE, &bytes_received)) != CURLE_OK ) { qvd_error(qvd, "An error ocurred in curl_easy_recv: %ul <%s>\n", qvd->res, curl_easy_strerror(qvd->res)); return 2; } qvd->buffer.data[bytes_received] = 0; qvd_printf("%d input received was <%s>\n", i, qvd->buffer.data); /* TODO what happens if for other strings V/qvd ( 7551): Before select on send socket is: 43 V/qvd ( 7551): 0 input received was <HTTP/1.1 403 Forbidden V/qvd ( 7551): Content-Type: text/plain V/qvd ( 7551): Content-Length: 56 V/qvd ( 7551): V/qvd ( 7551): > V/qvd ( 7551): 1 input received was <The requested virtual machine is offline for maintenance> */ if (strstr(qvd->buffer.data, "HTTP/1.1 101")) { qvd_printf("Upgrade of protocol was done\n"); break; } #define PROGRESSINFO "\r\nX-QVD-VM-Info: " if (strstr(qvd->buffer.data, "HTTP/1.1 102")) { qvd_printf("Progress message"); if ((ptr = strcasestr(qvd->buffer.data, PROGRESSINFO)) != NULL) { #ifdef TRACE qvd_printf("ptr is %s and size is %d", ptr, strlen(PROGRESSINFO)); #endif ptr += strlen(PROGRESSINFO); qvd_progress(qvd, ptr); } #ifdef TRACE else { qvd_printf("Pointer finding %s is null", PROGRESSINFO); } #endif } /* TODO cleanup printf */ if (strstr(qvd->buffer.data, "HTTP/1.1 2") || strstr(qvd->buffer.data, "HTTP/1.1 3") || strstr(qvd->buffer.data, "HTTP/1.1 4") || strstr(qvd->buffer.data, "HTTP/1.1 5")) { bytes_received_total = 0; #define CONTENT_LENGTH "\r\nContent-Length: " if ((ptr = strcasestr(qvd->buffer.data, CONTENT_LENGTH)) != NULL) { ptr += strlen(CONTENT_LENGTH); #ifdef TRACE qvd_printf("Parsing content length from <%s> and starting in <%s>", qvd->buffer.data, ptr); #endif content_length = -1; if (sscanf(ptr, "%d", &content_length) != 1) { qvd_printf("Error parsing content-length setting to -1: %d", content_length); content_length = -1; } } while (bytes_received < BUFFER_SIZE) { qvd_printf("Waiting for extra data after found 2xx, 3xx, 4xx or 5xx code <%s>", qvd->buffer.data); select(socket+1, &myset, &zero, &zero, NULL); ptr = qvd->buffer.data; ptr += bytes_received; /* TODO implement callback for info */ if ((qvd->res = curl_easy_recv(qvd->curl, ptr, BUFFER_SIZE, &bytes_received_total)) != CURLE_OK ) { ptr = strstr(qvd->buffer.data, "\r\n\r\n"); qvd_error(qvd, "Error received in qvd_curl_easy_recv: %d. <%s>", qvd->res, ptr); return 7; } bytes_received += bytes_received_total; #define DOUBLENEWLINE "\r\n\r\n" content = strstr(qvd->buffer.data, DOUBLENEWLINE); content_size_parsed = content != NULL ? strlen(content): -1; #ifdef TRACE qvd_printf("The bytes received were: %d, and curle code was: %d, content: <%s>, size of content: %d", bytes_received_total, qvd->res, content, content_size_parsed); #endif if (bytes_received == 0 || content_size_parsed >= content_length) { content += strlen(DOUBLENEWLINE); qvd_error(qvd, "Error: <%s>", content); return 8; } } } } if (i >=10 ) { qvd_error(qvd, "Error not received response for protocol upgrade in %d tries http/1.1\n", i); return 3; } return 0; }
/* Init and free functions */ qvdclient *qvd_init(const char *hostname, const int port, const char *username, const char *password) { qvdclient *qvd; qvd_printf("Starting qvd_init. %s", qvd_get_version_text); if (strlen(username) + strlen(password) + 2 > MAX_USERPWD) { qvd_error(qvd, "Length of username and password + 2 is longer than %d\n", MAX_USERPWD); return NULL; } if (strlen(hostname) + 6 + strlen("https:///") + 2 > MAX_BASEURL) { qvd_error(qvd, "Length of hostname and port + scheme + 2 is longer than %d\n", MAX_BASEURL); return NULL; } if (! (qvd = (qvdclient *) malloc(sizeof(qvdclient)))) { qvd_error(qvd, "Error allocating memory: %s", strerror(errno)); return NULL; } if (snprintf(qvd->userpwd, MAX_USERPWD, "%s:%s", username, password) >= MAX_USERPWD) { qvd_error(qvd, "Error initializing userpwd (string too long)\n"); free(qvd); return NULL; } if (_qvd_set_base64_auth(qvd)) { qvd_error(qvd, "Error initializing authdigest\n"); free(qvd); return NULL; } if (snprintf(qvd->baseurl, MAX_BASEURL, "https://%s:%d", hostname, port) >= MAX_BASEURL) { qvd_error(qvd, "Error initializing baseurl(string too long)\n"); free(qvd); return NULL; } if (snprintf(qvd->useragent, MAX_USERAGENT, "%s %s", DEFAULT_USERAGENT_PRODUCT, curl_version()) >= MAX_USERAGENT) { qvd_error(qvd, "Error initializing useragent (string too long)\n"); free(qvd); return NULL; } qvd->curl = curl_easy_init(); if (!qvd->curl) { qvd_error(qvd, "Error initializing curl\n"); free(qvd); return NULL; } qvd_printf("Curl pointer is %p", qvd->curl); if (get_debug_level()) { curl_easy_setopt(qvd->curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(qvd->curl, CURLOPT_DEBUGFUNCTION, qvd_curl_debug_callback); } curl_easy_setopt(qvd->curl, CURLOPT_ERRORBUFFER, qvd->error_buffer); /* curl_easy_setopt(qvd->curl, CURLOPT_SSL_VERIFYPEER, 1L); */ /* curl_easy_setopt(qvd->curl, CURLOPT_SSL_VERIFYHOST, 2L); */ curl_easy_setopt(qvd->curl, CURLOPT_CERTINFO, 1L); curl_easy_setopt(qvd->curl, CURLOPT_CAPATH, qvd->certpath); curl_easy_setopt(qvd->curl, CURLOPT_SSL_CTX_FUNCTION, _qvd_sslctxfun); curl_easy_setopt(qvd->curl, CURLOPT_SSL_CTX_DATA, (void *)qvd); /* curl_easy_setopt(qvd->curl, CURLOPT_CAINFO, NULL);*/ _qvd_ssl_index = SSL_CTX_get_ex_new_index(0, (void *)qvd, NULL, NULL, NULL); curl_easy_setopt(qvd->curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(qvd->curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(qvd->curl, CURLOPT_TCP_NODELAY, 1L); /* curl_easy_setopt(qvd->curl, CURLOPT_FAILONERROR, 1L);*/ curl_easy_setopt(qvd->curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_BASIC); curl_easy_setopt(qvd->curl, CURLOPT_USERPWD, qvd->userpwd); curl_easy_setopt(qvd->curl, CURLOPT_WRITEFUNCTION, _qvd_write_buffer_callback); curl_easy_setopt(qvd->curl, CURLOPT_WRITEDATA, &(qvd->buffer)); curl_easy_setopt(qvd->curl, CURLOPT_USERAGENT, qvd->useragent); /* If client certificate CURLOPT_SSLCERT , CURLOPT_SSLKEY, CURLOPT_SSLCERTTYPE "PEM" */ /* Copy parameters */ strncpy(qvd->hostname, hostname, MAX_BASEURL); qvd->hostname[MAX_BASEURL - 1] = '\0'; qvd->port = port; strncpy(qvd->username, username, MAX_USERPWD); qvd->username[MAX_USERPWD - 1] = '\0'; strncpy(qvd->password, password, MAX_USERPWD); qvd->password[MAX_USERPWD - 1] = '\0'; strncpy(qvd->client_cert, "", MAX_PATH_STRING); strncpy(qvd->client_key, "", MAX_PATH_STRING); qvd->use_client_cert = 0; qvd->numvms = 0; qvd_set_link(qvd, DEFAULT_LINK); qvd_set_geometry(qvd, DEFAULT_GEOMETRY); qvd_set_os(qvd, DEFAULT_OS); qvd->keyboard = "pc%2F105"; qvd->fullscreen = 0; qvd->print_enabled = 0; qvd->ssl_no_cert_check = 0; qvd->ssl_verify_callback = NULL; qvd->progress_callback = NULL; qvd->userdata = NULL; qvd->nx_options = NULL; *(qvd->display) = '\0'; *(qvd->home) = '\0'; strcpy(qvd->error_buffer, ""); QvdBufferInit(&(qvd->buffer)); if (!(qvd->vmlist = malloc(sizeof(vmlist)))) { free(qvd); return NULL; } QvdVmListInit(qvd->vmlist); return qvd; }
/* * _qvd_client_loop * -------------------- * | | * proxyFd ---| _qvd_client_loop |---connFd * (X display)| | (curl to remote host) * -------------------- * * ----- proxyRead ----> * <----- proxyWrite ---- * * We read from proxyFd and store it in the proxyRead buffer and then write it into connFd (curl) * We read from connFd and store it in the proxyWrite buffer and then write it ingo proxyFd (NX) * */ int _qvd_client_loop(qvdclient *qvd, int connFd, int proxyFd) { qvd_printf("_qvd_client_loop\n"); size_t read = 0, written = 0; struct timeval timeout; fd_set rfds, wfds; int ret, res, err, maxfds, numunsupportedprotocolerrs = 0, result = 0, i; QvdBuffer proxyWrite, proxyRead; qvd_printf("_qvd_client_loop(%p, %d, %d)\n", qvd, connFd, proxyFd); QvdBufferInit(&proxyWrite); QvdBufferInit(&proxyRead); do { ret = 0; timeout.tv_sec = QVDLOOP_TIMEOUT_SEC; timeout.tv_usec = QVDLOOP_TIMEOUT_USEC; maxfds = 1+MAX(connFd, proxyFd); FD_ZERO(&rfds); FD_ZERO(&wfds); if (proxyFd > 0 && QvdBufferCanRead(&proxyRead)) FD_SET(proxyFd, &rfds); if (connFd > 0 && QvdBufferCanRead(&proxyWrite)) FD_SET(connFd, &rfds); if (NXTransPrepare(&maxfds, &rfds, &wfds, &timeout)) { #ifdef TRACE qvd_printf("_qvd_client_loop: executing select()\n"); #endif NXTransSelect(&ret, &err, &maxfds, &rfds, &wfds, &timeout); NXTransExecute(&ret, &err, &maxfds, &rfds, &wfds, &timeout); } if (ret == -1 && errno == EINTR) continue; if (ret < 0) { qvd_error(qvd, "Error in _qvd_client_loop: select() %s\n", strerror(errno)); return 1; } if (qvd->end_connection) { qvd_printf("Connection ended with qvd_end_connection()."); qvd_progress(qvd, "Connection ended with qvd_end_connection()."); return 0; } #ifdef TRACE qvd_printf("isset proxyfd read: %d; connfd read: %d\n", FD_ISSET(proxyFd, &rfds), FD_ISSET(connFd, &rfds)); #endif /* Read from curl socket and store in proxyWrite buffer */ if (connFd > 0 && FD_ISSET(connFd, &rfds)) { read = 0; /* handle case of CURLE_UNSUPPORTED_PROTOCOL where read does not gets modified */ res = curl_easy_recv(qvd->curl, proxyWrite.data+proxyWrite.offset, BUFFER_SIZE-proxyWrite.size, &read); switch (res) { case CURLE_OK: #ifdef TRACE qvd_printf("curl: recv'd %ld\n", read); #endif proxyWrite.size += read; if (read == 0) { qvd_printf("Setting connFd to 0, End of stream\n"); connFd = -1; } numunsupportedprotocolerrs = 0; break; case CURLE_AGAIN: qvd_printf("Nothing read. receiving curl_easy_recv: %d CURLE_AGAIN, read %d\n", res, read); break; case CURLE_UNSUPPORTED_PROTOCOL: numunsupportedprotocolerrs++; qvd_printf("Unsupported protocol. receiving curl_easy_recv: %d CURLE_UNSUPPORTED_PROTOCOL (wait for next iteration), read %d, number of sequential errors=%d\n", res, read, numunsupportedprotocolerrs); qvd_printf("Error buffer: %s", qvd->error_buffer); #ifdef TRACE qvd_printf("curle_unsupported_protocol string size"); for (i=0; i < read; i++) qvd_printf("%x %c ",proxyWrite.data[i], proxyWrite.data[i]); qvd_printf("\n"); #endif # define MAX_CURLE_UNSUPPORTED_PROTOCOL 1 if (numunsupportedprotocolerrs >= MAX_CURLE_UNSUPPORTED_PROTOCOL) { qvd_error(qvd, "Unsupported protocol received %d times. receiving curl_easy_recv: %d CURLE_UNSUPPORTED_PROTOCOL (wait for next iteration), read %d, number of sequential errors=%d\n", MAX_CURLE_UNSUPPORTED_PROTOCOL, res, read, numunsupportedprotocolerrs); /* An error we need to finish the connection */ connFd = -1; /* proxyFd = -1; */ result = 0; } break; default: qvd_error(qvd, "Error receiving curl_easy_recv: %d\n", res); connFd = -1; /* proxyFd = -1; */ result = -1; } } /* Read from NX and store in proxyRead buffer */ if (proxyFd > 0 && FD_ISSET(proxyFd, &rfds)) { ret = QvdBufferRead(&proxyRead, proxyFd); if (ret == 0) { qvd_printf("No more bytes to read from proxyFd ending\n"); proxyFd = -1; } if (ret < 0) { qvd_error(qvd, "Error proxyFd read error: %s\n", strerror(errno)); proxyFd = -1; } } if (proxyFd > 0 && QvdBufferCanWrite(&proxyWrite)) { ret = QvdBufferWrite(&proxyWrite, proxyFd); if (ret < 0 && errno != EINTR) { qvd_error(qvd, "Error reading from proxyFd: %d %s\n", errno, strerror(errno)); proxyFd = -1; } } if (connFd > 0 && QvdBufferCanWrite(&proxyRead)) { /*QvdBufferWrite(&proxyRead, connFd);*/ res = curl_easy_send(qvd->curl, proxyRead.data+proxyRead.offset, proxyRead.size-proxyRead.offset, &written); switch (res) { case CURLE_OK: proxyRead.offset += written; #ifdef TRACE qvd_printf("curl: send'd %ld\n", written); #endif if (proxyRead.offset >= proxyRead.size) QvdBufferReset(&proxyRead); numunsupportedprotocolerrs = 0; break; case CURLE_AGAIN: qvd_printf("Nothing written, wait for next iteration. curl_easy_send: %d CURLE_AGAIN, written %d\n", res, written); break; case CURLE_UNSUPPORTED_PROTOCOL: numunsupportedprotocolerrs++; qvd_printf("Unsupported protocol sent %d times. sending curl_easy_sendv: %d CURLE_UNSUPPORTED_PROTOCOL (wait for next iteration), written %d, number of sequential errors=%d\n", MAX_CURLE_UNSUPPORTED_PROTOCOL, res, written, numunsupportedprotocolerrs); qvd_printf("Error buffer: %s", qvd->error_buffer); #ifdef TRACE qvd_printf("curle_unsupported_protocol string size"); for (i=0; i < written; i++) qvd_printf("%x %c ",proxyWrite.data[i], proxyWrite.data[i]); qvd_printf("\n"); #endif if (numunsupportedprotocolerrs >= MAX_CURLE_UNSUPPORTED_PROTOCOL) { qvd_error(qvd, "Unsupported protocol sent %d times. sending curl_easy_sendv: %d CURLE_UNSUPPORTED_PROTOCOL (wait for next iteration), written %d, number of sequential errors=%d\n", MAX_CURLE_UNSUPPORTED_PROTOCOL, res, written, numunsupportedprotocolerrs); /* An error we need to finish the connection */ /* TODO only finish connFd */ connFd = -1; /* proxyFd = -1; */ result = 0; } break; default: qvd_error(qvd, "Error sending curl_easy_send: %d", res); connFd = -1; } } } while (connFd > 0 && proxyFd > 0); return result; }
vmlist *qvd_list_of_vm(qvdclient *qvd) { char url[MAX_BASEURL]; int i; long http_code = 0; json_error_t error; char *command = "/qvd/list_of_vm"; if (!_qvd_set_certdir(qvd)) { qvd_printf("Please set the cert dir"); return NULL; } if (qvd->home && (*(qvd->home)) != '\0') { qvd_printf("Setting NX_HOME to %s\n", qvd->home); if (setenv("NX_HOME", qvd->home, 1)) { qvd_error(qvd, "Error setting NX_HOME to %s. errno: %d (%s)", qvd->home, errno, strerror(errno)); } } if (snprintf(url, MAX_BASEURL, "%s%s", qvd->baseurl, command) >= MAX_BASEURL) { qvd_error(qvd, "Error initializing url in list_of_vm, length is longer than %d\n", MAX_BASEURL); return NULL; } _qvd_use_client_cert(qvd); curl_easy_setopt(qvd->curl, CURLOPT_URL, url); /* curl_easy_setopt(curl, CURLOPT_WRITEDATA, &jsonBuffer); */ qvd->res = curl_easy_perform(qvd->curl); qvd_printf("After easy_perform: %ul\n", qvd->res); if (qvd->res) { qvd_printf("Error accessing url: <%s>, error code: %ul\n", url, qvd->res); qvd_error(qvd, "Error accessing list of VMs: %s\n", curl_easy_strerror(qvd->res)); return NULL; } curl_easy_getinfo (qvd->curl, CURLINFO_RESPONSE_CODE, &http_code); if (http_code == 401) { qvd_error(qvd, "Error authenticating user\n"); return NULL; } qvd_printf("No error and no auth error after curl_easy_perform\n"); /* QvdBufferInit(&(qvd->buffer)); */ json_t *vmList = json_loads(qvd->buffer.data, 0, &error); int arrayLength = json_array_size(vmList); qvd->numvms = arrayLength; qvd_printf("VMs available: %d\n", qvd->numvms); QvdVmListFree(qvd->vmlist); if (!(qvd->vmlist = malloc(sizeof(vmlist)))) { qvd_error(qvd, "Error allocating memory for vmlist"); return NULL; } QvdVmListInit(qvd->vmlist); for (i = 0; i < arrayLength; i++) { json_t *obj = json_array_get(vmList, i); int id, blocked; char *name, *state; json_unpack(obj, "{s:i,s:s,s:i,s:s}", "id", &id, "state", &state, "blocked", &blocked, "name", &name); qvd_printf("VM ID:%d NAME:%s STATE:%s BLOCKED:%d\n", id, name, state, blocked); QvdVmListAppendVm(qvd, qvd->vmlist, QvdVmNew(id, name, state, blocked)); } /* QvdBufferReset(&(qvd->buffer));*/ if (qvd->numvms <= 0) { qvd_error(qvd, "No virtual machines available for user %s\n", qvd->username); } else { qvd_progress(qvd, "Returning list of vms"); } return qvd->vmlist; }