Line data Source code
1 : /** 2 : * @file clustercon.c 3 : * @brief 4 : * 5 : * <+DETAILED+> 6 : * 7 : * @author François Cerbelle (Fanfan), francois@cerbelle.net 8 : * 9 : * @internal 10 : * Created: 25/10/2024 11 : * Revision: none 12 : * Last modified: 2024-11-13 21:40 13 : * Compiler: gcc 14 : * Organization: Cerbelle.net 15 : * Copyright: Copyright (c) 2024, François Cerbelle 16 : * 17 : * This source code is released for free distribution under the terms of the 18 : * GNU General Public License as published by the Free Software Foundation. 19 : */ 20 : 21 : #ifdef HAVE_CONFIG_H 22 : #include "config.h" 23 : #endif 24 : 25 : #include "clustercon.h" 26 : #include "base64.h" /* Base64 encoder and decoder */ 27 : 28 : #include <unistd.h> 29 : #include <string.h> 30 : #ifdef _WIN32 31 : #include <winsock2.h> 32 : #include <ws2tcpip.h> 33 : #else 34 : #include <arpa/inet.h> 35 : #endif 36 : #include <openssl/err.h> 37 : 38 : typedef struct rsclustercon_s { 39 : char* host; 40 : char* user; 41 : char* pass; 42 : unsigned short int insecure; 43 : char* cacert; 44 : int sock; 45 : SSL_CTX* ctx; 46 : SSL* ssl; 47 : } rsclustercon_t; 48 : 49 0 : rsclustercon_t* cluster_new(const cluster_t* cluster) { 50 0 : rsclustercon_t* rsclustercon = NULL; 51 : 52 0 : if (NULL==(rsclustercon=malloc(sizeof(struct rsclustercon_s)))) { 53 0 : perror("cluster_new"); 54 0 : return NULL; 55 : } 56 0 : if (NULL==(rsclustercon->host=strdup(cluster->host))) { 57 0 : perror("cluster_new host"); 58 0 : free(rsclustercon); 59 0 : return NULL; 60 : } 61 0 : if (NULL==(rsclustercon->user=strdup(cluster->user))) { 62 0 : perror("cluster_new user"); 63 0 : free(rsclustercon->host); 64 0 : free(rsclustercon); 65 0 : return NULL; 66 : } 67 0 : if (NULL==(rsclustercon->pass=strdup(cluster->pass))) { 68 0 : perror("cluster_new pass"); 69 0 : free(rsclustercon->user); 70 0 : free(rsclustercon->host); 71 0 : free(rsclustercon); 72 0 : return NULL; 73 : } 74 0 : if (NULL==(rsclustercon->cacert=strdup(cluster->cacert))) { 75 0 : perror("cluster_new cacert"); 76 0 : free(rsclustercon->pass); 77 0 : free(rsclustercon->user); 78 0 : free(rsclustercon->host); 79 0 : free(rsclustercon); 80 0 : return NULL; 81 : } 82 0 : rsclustercon->insecure = strcmp(cluster->insecure,"false"); 83 0 : return rsclustercon; 84 : } 85 : 86 0 : int cluster_open(rsclustercon_t* rsclustercon) { 87 : struct sockaddr_in server_addr; 88 : 89 : /* Socket creation */ 90 0 : rsclustercon->sock = socket(AF_INET, SOCK_STREAM, 0); 91 0 : if (rsclustercon->sock < 0) { 92 0 : perror("Socket creation error"); 93 0 : return 1; 94 : } 95 : 96 0 : server_addr.sin_family = AF_INET; 97 0 : server_addr.sin_port = htons(9443); 98 0 : if (inet_pton(AF_INET, rsclustercon->host, &server_addr.sin_addr) <= 0) { 99 0 : perror("Invalid address"); 100 0 : return 2; 101 : } 102 : 103 : /* Server connection */ 104 0 : if (connect(rsclustercon->sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) { 105 0 : perror("Connection failed"); 106 0 : return 3; 107 : } 108 : 109 : /* OpenSSL initialization */ 110 0 : SSL_library_init(); 111 : /* OpenSSL_add_all_algorithms(); */ 112 0 : OpenSSL_add_ssl_algorithms(); 113 0 : SSL_load_error_strings(); 114 : 115 : /* Initialize OpenSSL context */ 116 0 : rsclustercon->ctx = SSL_CTX_new(SSLv23_client_method()); 117 0 : if (!rsclustercon->ctx) { 118 0 : ERR_print_errors_fp(stderr); 119 0 : return 4; 120 : } 121 : 122 : /* Load server certificate or certificate from a certification authority (CA) */ 123 0 : if (strcmp("",rsclustercon->cacert)&&rsclustercon->insecure==0) 124 0 : if (SSL_CTX_load_verify_locations(rsclustercon->ctx, rsclustercon->cacert, NULL) != 1) { 125 0 : ERR_print_errors_fp(stderr); 126 0 : return 5; 127 : } 128 : 129 0 : if (rsclustercon->insecure) 130 : /* Disable certificat check */ 131 0 : SSL_CTX_set_verify(rsclustercon->ctx, SSL_VERIFY_NONE, NULL); 132 : else 133 : /* Enable certificat check */ 134 0 : SSL_CTX_set_verify(rsclustercon->ctx, SSL_VERIFY_PEER, NULL); 135 : 136 : 137 : /* Link SSL configuration to the socket */ 138 0 : rsclustercon->ssl = SSL_new(rsclustercon->ctx); 139 0 : SSL_set_fd(rsclustercon->ssl, rsclustercon->sock); 140 : 141 : /* Initiate SSL connection */ 142 0 : if (SSL_connect(rsclustercon->ssl) <= 0) { 143 0 : ERR_print_errors_fp(stderr); 144 0 : return 6; 145 : } 146 : 147 0 : return 0; 148 : } 149 : 150 0 : cJSON* cluster_queryget(const rsclustercon_t* rsclustercon, const char* endpoint) { 151 : char buf[1024]; 152 : 153 : /* Prepare basic authentication */ 154 : char* auth_encoded; 155 : { 156 : char* auth_clear; 157 0 : if (NULL==(auth_clear = (char*)malloc(strlen(rsclustercon->user)+1+strlen(rsclustercon->pass)+1))) { 158 0 : perror("cluster_queryget malloc(auth)"); 159 0 : return NULL; 160 : }; 161 0 : strcpy(auth_clear,rsclustercon->user); 162 0 : strcat(auth_clear,":"); 163 0 : strcat(auth_clear,rsclustercon->pass); 164 0 : auth_encoded=base64_encode(auth_clear); 165 0 : free(auth_clear); 166 : } 167 : 168 : /* Prepare query */ 169 : char http_request[512]; 170 0 : snprintf(http_request, sizeof(http_request), 171 : "GET %s HTTP/1.1\r\n" 172 : "Host: %s\r\n" 173 : "Content-Type: application/json\r\n" 174 : "Authorization: Basic %s\r\n" 175 : "Connection: close\r\n\r\n", 176 0 : endpoint,rsclustercon->host,auth_encoded 177 : ); 178 0 : free(auth_encoded); 179 : 180 : /* Send query */ 181 0 : SSL_write(rsclustercon->ssl, http_request, strlen(http_request)); 182 : 183 : /* Read reply */ 184 0 : int bytes=0; 185 0 : char* reply=NULL; 186 0 : if (NULL==(reply=strdup(""))) { 187 0 : perror("cluster_queryget strdup"); 188 0 : return NULL; 189 : }; 190 0 : while ((bytes=SSL_read(rsclustercon->ssl, buf, 1024 - 1)) > 0) { 191 0 : buf[bytes] = 0; 192 : char* newreply; 193 0 : if ((newreply=(char*)realloc(reply,strlen(reply)+bytes+1))==NULL) { 194 0 : perror("Unable to allocate reply buffer"); 195 0 : free(reply); 196 0 : return NULL; 197 : } else 198 0 : reply = newreply; 199 0 : strcat(reply, buf); 200 : } 201 : 202 : /* Remove HTTP headers */ 203 : char* http_body; 204 : char* retval_txt; 205 0 : if (NULL==(http_body = strstr(reply,"\r\n\r\n"))) 206 0 : retval_txt= NULL; 207 : else 208 0 : retval_txt=strdup(http_body); 209 0 : free(reply); 210 : 211 0 : cJSON* retval_json = cJSON_Parse(retval_txt); 212 0 : free(retval_txt); 213 : 214 0 : return retval_json; 215 : } 216 : 217 0 : void cluster_close(rsclustercon_t* rsclustercon) { 218 0 : SSL_shutdown(rsclustercon->ssl); 219 0 : SSL_free(rsclustercon->ssl); 220 0 : close(rsclustercon->sock); 221 0 : SSL_CTX_free(rsclustercon->ctx); 222 0 : EVP_cleanup(); 223 0 : } 224 : 225 0 : void cluster_del(rsclustercon_t* rsclustercon) { 226 0 : free(rsclustercon->cacert); 227 0 : free(rsclustercon->pass); 228 0 : free(rsclustercon->user); 229 0 : free(rsclustercon->host); 230 0 : free(rsclustercon); 231 0 : } 232 : 233 : /* vim: set tw=80: */