cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

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