socket.c (9662B)
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20#include "config.h" 21 22#include "guacamole/mem.h" 23#include "guacamole/error.h" 24#include "guacamole/protocol.h" 25#include "guacamole/socket.h" 26#include "guacamole/timestamp.h" 27 28#include <inttypes.h> 29#include <pthread.h> 30#include <stddef.h> 31#include <stdint.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <time.h> 36#include <unistd.h> 37 38char __guac_socket_BASE64_CHARACTERS[64] = { 39 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 40 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 41 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 42 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', 43 '8', '9', '+', '/' 44}; 45 46static void* __guac_socket_keep_alive_thread(void* data) { 47 48 int old_cancelstate; 49 50 /* Calculate sleep interval */ 51 struct timespec interval; 52 interval.tv_sec = GUAC_SOCKET_KEEP_ALIVE_INTERVAL / 1000; 53 interval.tv_nsec = (GUAC_SOCKET_KEEP_ALIVE_INTERVAL % 1000) * 1000000L; 54 55 /* Socket keep-alive loop */ 56 guac_socket* socket = (guac_socket*) data; 57 while (socket->state == GUAC_SOCKET_OPEN) { 58 59 /* Send NOP keep-alive if it's been a while since the last output */ 60 guac_timestamp timestamp = guac_timestamp_current(); 61 if (timestamp - socket->last_write_timestamp > 62 GUAC_SOCKET_KEEP_ALIVE_INTERVAL) { 63 64 /* Send NOP */ 65 if (guac_protocol_send_nop(socket) 66 || guac_socket_flush(socket)) 67 break; 68 69 } 70 71 /* Sleep until next keep-alive check, but allow thread cancellation 72 * during that sleep */ 73 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate); 74 nanosleep(&interval, NULL); 75 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate); 76 77 } 78 79 return NULL; 80 81} 82 83static ssize_t __guac_socket_write(guac_socket* socket, 84 const void* buf, size_t count) { 85 86 /* Update timestamp of last write */ 87 socket->last_write_timestamp = guac_timestamp_current(); 88 89 /* If handler defined, call it. */ 90 if (socket->write_handler) 91 return socket->write_handler(socket, buf, count); 92 93 /* Otherwise, pretend everything was written. */ 94 return count; 95 96} 97 98ssize_t guac_socket_write(guac_socket* socket, 99 const void* buf, size_t count) { 100 101 const char* buffer = buf; 102 103 /* Write until completely written */ 104 while (count > 0) { 105 106 /* Attempt to write, return on error */ 107 int written = __guac_socket_write(socket, buffer, count); 108 if (written == -1) 109 return 1; 110 111 /* Advance buffer as data written */ 112 buffer += written; 113 count -= written; 114 115 } 116 117 return 0; 118 119} 120 121ssize_t guac_socket_read(guac_socket* socket, void* buf, size_t count) { 122 123 /* If handler defined, call it. */ 124 if (socket->read_handler) 125 return socket->read_handler(socket, buf, count); 126 127 /* Otherwise, pretend nothing was read. */ 128 return 0; 129 130} 131 132int guac_socket_select(guac_socket* socket, int usec_timeout) { 133 134 /* Call select handler if defined */ 135 if (socket->select_handler) 136 return socket->select_handler(socket, usec_timeout); 137 138 /* Otherwise, assume ready. */ 139 return 1; 140 141} 142 143guac_socket* guac_socket_alloc() { 144 145 guac_socket* socket = guac_mem_alloc(sizeof(guac_socket)); 146 147 /* If no memory available, return with error */ 148 if (socket == NULL) { 149 guac_error = GUAC_STATUS_NO_MEMORY; 150 guac_error_message = "Could not allocate memory for socket"; 151 return NULL; 152 } 153 154 socket->__ready = 0; 155 socket->data = NULL; 156 socket->state = GUAC_SOCKET_OPEN; 157 socket->last_write_timestamp = guac_timestamp_current(); 158 159 /* No keep alive ping by default */ 160 socket->__keep_alive_enabled = 0; 161 162 /* No handlers yet */ 163 socket->read_handler = NULL; 164 socket->write_handler = NULL; 165 socket->select_handler = NULL; 166 socket->free_handler = NULL; 167 socket->flush_handler = NULL; 168 socket->lock_handler = NULL; 169 socket->unlock_handler = NULL; 170 171 return socket; 172 173} 174 175void guac_socket_require_keep_alive(guac_socket* socket) { 176 177 /* Start keep-alive thread */ 178 socket->__keep_alive_enabled = 1; 179 pthread_create(&(socket->__keep_alive_thread), NULL, 180 __guac_socket_keep_alive_thread, (void*) socket); 181 182} 183 184void guac_socket_instruction_begin(guac_socket* socket) { 185 186 /* Call instruction begin handler if defined */ 187 if (socket->lock_handler) 188 socket->lock_handler(socket); 189 190} 191 192void guac_socket_instruction_end(guac_socket* socket) { 193 194 /* Call instruction end handler if defined */ 195 if (socket->unlock_handler) 196 socket->unlock_handler(socket); 197 198} 199 200void guac_socket_free(guac_socket* socket) { 201 202 guac_socket_flush(socket); 203 204 /* Call free handler if defined */ 205 if (socket->free_handler) 206 socket->free_handler(socket); 207 208 /* Mark as closed */ 209 socket->state = GUAC_SOCKET_CLOSED; 210 211 /* Stop keep-alive thread, if enabled */ 212 if (socket->__keep_alive_enabled) { 213 pthread_cancel(socket->__keep_alive_thread); 214 pthread_join(socket->__keep_alive_thread, NULL); 215 } 216 217 guac_mem_free(socket); 218} 219 220ssize_t guac_socket_write_int(guac_socket* socket, int64_t i) { 221 222 char buffer[128]; 223 int length; 224 225 /* Write provided integer as a string */ 226 length = snprintf(buffer, sizeof(buffer), "%"PRIi64, i); 227 return guac_socket_write(socket, buffer, length); 228 229} 230 231ssize_t guac_socket_write_string(guac_socket* socket, const char* str) { 232 233 /* Write contents of string */ 234 if (guac_socket_write(socket, str, strlen(str))) 235 return 1; 236 237 return 0; 238 239} 240 241ssize_t __guac_socket_write_base64_triplet(guac_socket* socket, 242 int a, int b, int c) { 243 244 char output[4]; 245 246 /* Byte 0:[AAAAAA] AABBBB BBBBCC CCCCCC */ 247 output[0] = __guac_socket_BASE64_CHARACTERS[(a & 0xFC) >> 2]; 248 249 if (b >= 0) { 250 251 /* Byte 1: AAAAAA [AABBBB] BBBBCC CCCCCC */ 252 output[1] = __guac_socket_BASE64_CHARACTERS[((a & 0x03) << 4) | ((b & 0xF0) >> 4)]; 253 254 /* 255 * Bytes 2 and 3, zero characters of padding: 256 * 257 * AAAAAA AABBBB [BBBBCC] CCCCCC 258 * AAAAAA AABBBB BBBBCC [CCCCCC] 259 */ 260 if (c >= 0) { 261 output[2] = __guac_socket_BASE64_CHARACTERS[((b & 0x0F) << 2) | ((c & 0xC0) >> 6)]; 262 output[3] = __guac_socket_BASE64_CHARACTERS[c & 0x3F]; 263 } 264 265 /* 266 * Bytes 2 and 3, one character of padding: 267 * 268 * AAAAAA AABBBB [BBBB--] ------ 269 * AAAAAA AABBBB BBBB-- [------] 270 */ 271 else { 272 output[2] = __guac_socket_BASE64_CHARACTERS[((b & 0x0F) << 2)]; 273 output[3] = '='; 274 } 275 } 276 277 /* 278 * Bytes 1, 2, and 3, two characters of padding: 279 * 280 * AAAAAA [AA----] ------ ------ 281 * AAAAAA AA---- [------] ------ 282 * AAAAAA AA---- ------ [------] 283 */ 284 else { 285 output[1] = __guac_socket_BASE64_CHARACTERS[((a & 0x03) << 4)]; 286 output[2] = '='; 287 output[3] = '='; 288 } 289 290 /* At this point, 4 base64 bytes have been written */ 291 if (guac_socket_write(socket, output, 4)) 292 return -1; 293 294 /* If no second byte was provided, only one byte was written */ 295 if (b < 0) 296 return 1; 297 298 /* If no third byte was provided, only two bytes were written */ 299 if (c < 0) 300 return 2; 301 302 /* Otherwise, three bytes were written */ 303 return 3; 304 305} 306 307ssize_t __guac_socket_write_base64_byte(guac_socket* socket, int buf) { 308 309 int* __ready_buf = socket->__ready_buf; 310 311 int retval; 312 313 __ready_buf[socket->__ready++] = buf; 314 315 /* Flush triplet */ 316 if (socket->__ready == 3) { 317 retval = __guac_socket_write_base64_triplet(socket, __ready_buf[0], __ready_buf[1], __ready_buf[2]); 318 if (retval < 0) 319 return retval; 320 321 socket->__ready = 0; 322 } 323 324 return 1; 325} 326 327ssize_t guac_socket_write_base64(guac_socket* socket, const void* buf, size_t count) { 328 329 int retval; 330 331 const unsigned char* char_buf = (const unsigned char*) buf; 332 const unsigned char* end = char_buf + count; 333 334 while (char_buf < end) { 335 336 retval = __guac_socket_write_base64_byte(socket, *(char_buf++)); 337 if (retval < 0) 338 return retval; 339 340 } 341 342 return 0; 343 344} 345 346ssize_t guac_socket_flush(guac_socket* socket) { 347 348 /* If handler defined, call it. */ 349 if (socket->flush_handler) 350 return socket->flush_handler(socket); 351 352 /* Otherwise, do nothing */ 353 return 0; 354 355} 356 357ssize_t guac_socket_flush_base64(guac_socket* socket) { 358 359 int retval; 360 361 /* Flush triplet to output buffer */ 362 while (socket->__ready > 0) { 363 364 retval = __guac_socket_write_base64_byte(socket, -1); 365 if (retval < 0) 366 return retval; 367 368 } 369 370 return 0; 371 372} 373