socket-wsa.c (12447B)
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 "guacamole/mem.h" 21#include "guacamole/error.h" 22#include "guacamole/socket.h" 23 24#include <pthread.h> 25#include <stddef.h> 26#include <stdio.h> 27#include <stdlib.h> 28#include <string.h> 29#include <unistd.h> 30 31#include <winsock2.h> 32 33/** 34 * Data associated with an open socket which uses the Windows Socket API. 35 */ 36typedef struct guac_socket_wsa_data { 37 38 /** 39 * The associated Windows socket handle. 40 */ 41 SOCKET sock; 42 43 /** 44 * The number of bytes currently in the main write buffer. 45 */ 46 int written; 47 48 /** 49 * The main write buffer. Bytes written go here before being flushed 50 * to the open socket. 51 */ 52 char out_buf[GUAC_SOCKET_OUTPUT_BUFFER_SIZE]; 53 54 /** 55 * Lock which is acquired when an instruction is being written, and 56 * released when the instruction is finished being written. 57 */ 58 pthread_mutex_t socket_lock; 59 60 /** 61 * Lock which protects access to the internal buffer of this socket, 62 * guaranteeing atomicity of writes and flushes. 63 */ 64 pthread_mutex_t buffer_lock; 65 66} guac_socket_wsa_data; 67 68/** 69 * Writes the entire contents of the given buffer to the SOCKET handle 70 * associated with the given socket, retrying as necessary until the whole 71 * buffer is written, and aborting if an error occurs. 72 * 73 * @param socket 74 * The guac_socket associated with the SOCKET handle to which the given 75 * buffer should be written. 76 * 77 * @param buf 78 * The buffer of data to write to the given guac_socket. 79 * 80 * @param count 81 * The number of bytes within the given buffer. 82 * 83 * @return 84 * The number of bytes written, which will be exactly the size of the given 85 * buffer, or a negative value if an error occurs. 86 */ 87ssize_t guac_socket_wsa_write(guac_socket* socket, 88 const void* buf, size_t count) { 89 90 guac_socket_wsa_data* data = (guac_socket_wsa_data*) socket->data; 91 const char* buffer = buf; 92 93 /* Write until completely written */ 94 while (count > 0) { 95 96 int retval = send(data->sock, buffer, count, 0); 97 98 /* Record errors in guac_error */ 99 if (retval < 0) { 100 guac_error = GUAC_STATUS_SEE_ERRNO; 101 guac_error_message = "Error writing data to socket"; 102 return retval; 103 } 104 105 /* Advance buffer as data retval */ 106 buffer += retval; 107 count -= retval; 108 109 } 110 111 return 0; 112 113} 114 115/** 116 * Attempts to read from the underlying SOCKET handle of the given guac_socket, 117 * populating the given buffer. 118 * 119 * @param socket 120 * The guac_socket being read from. 121 * 122 * @param buf 123 * The arbitrary buffer which we must populate with data. 124 * 125 * @param count 126 * The maximum number of bytes to read into the buffer. 127 * 128 * @return 129 * The number of bytes read, or -1 if an error occurs. 130 */ 131static ssize_t guac_socket_wsa_read_handler(guac_socket* socket, 132 void* buf, size_t count) { 133 134 guac_socket_wsa_data* data = (guac_socket_wsa_data*) socket->data; 135 136 /* Read from socket */ 137 int retval = recv(data->sock, buf, count, 0); 138 139 /* Record errors in guac_error */ 140 if (retval < 0) { 141 guac_error = GUAC_STATUS_SEE_ERRNO; 142 guac_error_message = "Error reading data from socket"; 143 } 144 145 return retval; 146 147} 148 149/** 150 * Flushes the contents of the output buffer of the given socket immediately, 151 * without first locking access to the output buffer. This function must ONLY 152 * be called if the buffer lock has already been acquired. 153 * 154 * @param socket 155 * The guac_socket to flush. 156 * 157 * @return 158 * Zero if the flush operation was successful, non-zero otherwise. 159 */ 160static ssize_t guac_socket_wsa_flush(guac_socket* socket) { 161 162 guac_socket_wsa_data* data = (guac_socket_wsa_data*) socket->data; 163 164 /* Flush remaining bytes in buffer */ 165 if (data->written > 0) { 166 167 /* Write ALL bytes in buffer immediately */ 168 if (guac_socket_wsa_write(socket, data->out_buf, data->written)) 169 return 1; 170 171 data->written = 0; 172 } 173 174 return 0; 175 176} 177 178/** 179 * Flushes the internal buffer of the given guac_socket, writing all data 180 * to the underlying SOCKET handle. 181 * 182 * @param socket 183 * The guac_socket to flush. 184 * 185 * @return 186 * Zero if the flush operation was successful, non-zero otherwise. 187 */ 188static ssize_t guac_socket_wsa_flush_handler(guac_socket* socket) { 189 190 int retval; 191 guac_socket_wsa_data* data = (guac_socket_wsa_data*) socket->data; 192 193 /* Acquire exclusive access to buffer */ 194 pthread_mutex_lock(&(data->buffer_lock)); 195 196 /* Flush contents of buffer */ 197 retval = guac_socket_wsa_flush(socket); 198 199 /* Relinquish exclusive access to buffer */ 200 pthread_mutex_unlock(&(data->buffer_lock)); 201 202 return retval; 203 204} 205 206/** 207 * Writes the contents of the buffer to the output buffer of the given socket, 208 * flushing the output buffer as necessary, without first locking access to the 209 * output buffer. This function must ONLY be called if the buffer lock has 210 * already been acquired. 211 * 212 * @param socket 213 * The guac_socket to write the given buffer to. 214 * 215 * @param buf 216 * The buffer to write to the given socket. 217 * 218 * @param count 219 * The number of bytes in the given buffer. 220 * 221 * @return 222 * The number of bytes written, or a negative value if an error occurs 223 * during write. 224 */ 225static ssize_t guac_socket_wsa_write_buffered(guac_socket* socket, 226 const void* buf, size_t count) { 227 228 size_t original_count = count; 229 const char* current = buf; 230 guac_socket_wsa_data* data = (guac_socket_wsa_data*) socket->data; 231 232 /* Append to buffer, flush if necessary */ 233 while (count > 0) { 234 235 int chunk_size; 236 int remaining = sizeof(data->out_buf) - data->written; 237 238 /* If no space left in buffer, flush and retry */ 239 if (remaining == 0) { 240 241 /* Abort if error occurs during flush */ 242 if (guac_socket_wsa_flush(socket)) 243 return -1; 244 245 /* Retry buffer append */ 246 continue; 247 248 } 249 250 /* Calculate size of chunk to be written to buffer */ 251 chunk_size = count; 252 if (chunk_size > remaining) 253 chunk_size = remaining; 254 255 /* Update output buffer */ 256 memcpy(data->out_buf + data->written, current, chunk_size); 257 data->written += chunk_size; 258 259 /* Update provided buffer */ 260 current += chunk_size; 261 count -= chunk_size; 262 263 } 264 265 /* All bytes have been written, possibly some to the internal buffer */ 266 return original_count; 267 268} 269 270/** 271 * Appends the provided data to the internal buffer for future writing. The 272 * actual write attempt will occur only upon flush, or when the internal buffer 273 * is full. 274 * 275 * @param socket 276 * The guac_socket being write to. 277 * 278 * @param buf 279 * The arbitrary buffer containing the data to be written. 280 * 281 * @param count 282 * The number of bytes contained within the buffer. 283 * 284 * @return 285 * The number of bytes written, or -1 if an error occurs. 286 */ 287static ssize_t guac_socket_wsa_write_handler(guac_socket* socket, 288 const void* buf, size_t count) { 289 290 int retval; 291 guac_socket_wsa_data* data = (guac_socket_wsa_data*) socket->data; 292 293 /* Acquire exclusive access to buffer */ 294 pthread_mutex_lock(&(data->buffer_lock)); 295 296 /* Write provided data to buffer */ 297 retval = guac_socket_wsa_write_buffered(socket, buf, count); 298 299 /* Relinquish exclusive access to buffer */ 300 pthread_mutex_unlock(&(data->buffer_lock)); 301 302 return retval; 303 304} 305 306/** 307 * Waits for data on the underlying SOCKET handle of the given socket to 308 * become available such that the next read operation will not block. 309 * 310 * @param socket 311 * The guac_socket to wait for. 312 * 313 * @param usec_timeout 314 * The maximum amount of time to wait for data, in microseconds, or -1 to 315 * potentially wait forever. 316 * 317 * @return 318 * A positive value on success, zero if the timeout elapsed and no data is 319 * available, or a negative value if an error occurs. 320 */ 321static int guac_socket_wsa_select_handler(guac_socket* socket, 322 int usec_timeout) { 323 324 guac_socket_wsa_data* data = (guac_socket_wsa_data*) socket->data; 325 326 fd_set sockets; 327 struct timeval timeout; 328 int retval; 329 330 /* Initialize fd_set with single underlying socket handle */ 331 FD_ZERO(&sockets); 332 FD_SET(data->sock, &sockets); 333 334 /* No timeout if usec_timeout is negative */ 335 if (usec_timeout < 0) 336 retval = select(0, &sockets, NULL, NULL, NULL); 337 338 /* Handle timeout if specified */ 339 else { 340 timeout.tv_sec = usec_timeout / 1000000; 341 timeout.tv_usec = usec_timeout % 1000000; 342 retval = select(0, &sockets, NULL, NULL, &timeout); 343 } 344 345 /* Properly set guac_error */ 346 if (retval < 0) { 347 guac_error = GUAC_STATUS_SEE_ERRNO; 348 guac_error_message = "Error while waiting for data on socket"; 349 } 350 351 if (retval == 0) { 352 guac_error = GUAC_STATUS_TIMEOUT; 353 guac_error_message = "Timeout while waiting for data on socket"; 354 } 355 356 return retval; 357 358} 359 360/** 361 * Frees all implementation-specific data associated with the given socket, but 362 * not the socket object itself. 363 * 364 * @param socket 365 * The guac_socket whose associated data should be freed. 366 * 367 * @return 368 * Zero if the data was successfully freed, non-zero otherwise. This 369 * implementation always succeeds, and will always return zero. 370 */ 371static int guac_socket_wsa_free_handler(guac_socket* socket) { 372 373 guac_socket_wsa_data* data = (guac_socket_wsa_data*) socket->data; 374 375 /* Destroy locks */ 376 pthread_mutex_destroy(&(data->socket_lock)); 377 pthread_mutex_destroy(&(data->buffer_lock)); 378 379 /* Close socket */ 380 closesocket(data->sock); 381 382 guac_mem_free(data); 383 return 0; 384 385} 386 387/** 388 * Acquires exclusive access to the given socket. 389 * 390 * @param socket 391 * The guac_socket to which exclusive access is required. 392 */ 393static void guac_socket_wsa_lock_handler(guac_socket* socket) { 394 395 guac_socket_wsa_data* data = (guac_socket_wsa_data*) socket->data; 396 397 /* Acquire exclusive access to socket */ 398 pthread_mutex_lock(&(data->socket_lock)); 399 400} 401 402/** 403 * Relinquishes exclusive access to the given socket. 404 * 405 * @param socket 406 * The guac_socket to which exclusive access is no longer required. 407 */ 408static void guac_socket_wsa_unlock_handler(guac_socket* socket) { 409 410 guac_socket_wsa_data* data = (guac_socket_wsa_data*) socket->data; 411 412 /* Relinquish exclusive access to socket */ 413 pthread_mutex_unlock(&(data->socket_lock)); 414 415} 416 417guac_socket* guac_socket_open_wsa(SOCKET sock) { 418 419 pthread_mutexattr_t lock_attributes; 420 421 /* Allocate socket and associated data */ 422 guac_socket* socket = guac_socket_alloc(); 423 guac_socket_wsa_data* data = guac_mem_alloc(sizeof(guac_socket_wsa_data)); 424 425 /* Store socket as socket data */ 426 data->sock = sock; 427 data->written = 0; 428 socket->data = data; 429 430 pthread_mutexattr_init(&lock_attributes); 431 pthread_mutexattr_setpshared(&lock_attributes, PTHREAD_PROCESS_SHARED); 432 433 /* Init locks */ 434 pthread_mutex_init(&(data->socket_lock), &lock_attributes); 435 pthread_mutex_init(&(data->buffer_lock), &lock_attributes); 436 437 /* Set read/write handlers */ 438 socket->read_handler = guac_socket_wsa_read_handler; 439 socket->write_handler = guac_socket_wsa_write_handler; 440 socket->select_handler = guac_socket_wsa_select_handler; 441 socket->lock_handler = guac_socket_wsa_lock_handler; 442 socket->unlock_handler = guac_socket_wsa_unlock_handler; 443 socket->flush_handler = guac_socket_wsa_flush_handler; 444 socket->free_handler = guac_socket_wsa_free_handler; 445 446 return socket; 447 448} 449