socket-tee.c (6613B)
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/socket.h" 24 25#include <stdlib.h> 26 27/** 28 * Data specific to the tee implementation of guac_socket. 29 */ 30typedef struct guac_socket_tee_data { 31 32 /** 33 * The guac_socket to which all socket operations should be delegated. 34 */ 35 guac_socket* primary; 36 37 /** 38 * The guac_socket to which all write and flush operations should be 39 * duplicated. 40 */ 41 guac_socket* secondary; 42 43} guac_socket_tee_data; 44 45/** 46 * Callback function which reads only from the primary socket. 47 * 48 * @param socket 49 * The tee socket to read from. 50 * 51 * @param buf 52 * The buffer to read data into. 53 * 54 * @param count 55 * The maximum number of bytes to read into the given buffer. 56 * 57 * @return 58 * The value returned by guac_socket_read() when invoked on the primary 59 * socket with the given parameters. 60 */ 61static ssize_t __guac_socket_tee_read_handler(guac_socket* socket, 62 void* buf, size_t count) { 63 64 guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data; 65 66 /* Delegate read to wrapped socket */ 67 return guac_socket_read(data->primary, buf, count); 68 69} 70 71/** 72 * Callback function which writes the given data to both underlying sockets, 73 * returning only the result from the primary socket. 74 * 75 * @param socket 76 * The tee socket to write through. 77 * 78 * @param buf 79 * The buffer of data to write. 80 * 81 * @param count 82 * The number of bytes in the buffer to be written. 83 * 84 * @return 85 * The number of bytes written if the write was successful, or -1 if an 86 * error occurs. 87 */ 88static ssize_t __guac_socket_tee_write_handler(guac_socket* socket, 89 const void* buf, size_t count) { 90 91 guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data; 92 93 /* Write to secondary socket (ignoring result) */ 94 guac_socket_write(data->secondary, buf, count); 95 96 /* Delegate write to wrapped socket */ 97 if (guac_socket_write(data->primary, buf, count)) 98 return -1; 99 100 /* All data written successfully */ 101 return count; 102 103} 104 105/** 106 * Callback function which flushes both underlying sockets, returning only the 107 * result from the primary socket. 108 * 109 * @param socket 110 * The tee socket to flush. 111 * 112 * @return 113 * The value returned by guac_socket_flush() when invoked on the primary 114 * socket. 115 */ 116static ssize_t __guac_socket_tee_flush_handler(guac_socket* socket) { 117 118 guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data; 119 120 /* Flush secondary socket (ignoring result) */ 121 guac_socket_flush(data->secondary); 122 123 /* Delegate flush to wrapped socket */ 124 return guac_socket_flush(data->primary); 125 126} 127 128/** 129 * Callback function which delegates the lock operation to the primary 130 * socket alone. 131 * 132 * @param socket 133 * The tee socket on which guac_socket_instruction_begin() was invoked. 134 */ 135static void __guac_socket_tee_lock_handler(guac_socket* socket) { 136 137 guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data; 138 139 /* Delegate lock to wrapped sockets */ 140 guac_socket_instruction_begin(data->primary); 141 guac_socket_instruction_begin(data->secondary); 142 143} 144 145/** 146 * Callback function which delegates the unlock operation to the primary 147 * socket alone. 148 * 149 * @param socket 150 * The tee socket on which guac_socket_instruction_end() was invoked. 151 */ 152static void __guac_socket_tee_unlock_handler(guac_socket* socket) { 153 154 guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data; 155 156 /* Delegate unlock to wrapped sockets */ 157 guac_socket_instruction_end(data->secondary); 158 guac_socket_instruction_end(data->primary); 159 160} 161 162/** 163 * Callback function which delegates the select operation to the primary 164 * socket alone. 165 * 166 * @param socket 167 * The tee socket on which guac_socket_select() was invoked. 168 * 169 * @param usec_timeout 170 * The timeout to specify when invoking guac_socket_select() on the 171 * primary socket. 172 * 173 * @return 174 * The value returned by guac_socket_select() when invoked with the 175 * given parameters on the primary socket. 176 */ 177static int __guac_socket_tee_select_handler(guac_socket* socket, 178 int usec_timeout) { 179 180 guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data; 181 182 /* Delegate select to wrapped socket */ 183 return guac_socket_select(data->primary, usec_timeout); 184 185} 186 187/** 188 * Callback function which frees all underlying data associated with the 189 * given tee socket, including both primary and secondary sockets. 190 * 191 * @param socket 192 * The tee socket being freed. 193 * 194 * @return 195 * Always zero. 196 */ 197static int __guac_socket_tee_free_handler(guac_socket* socket) { 198 199 guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data; 200 201 /* Free underlying sockets */ 202 guac_socket_free(data->primary); 203 guac_socket_free(data->secondary); 204 205 /* Freeing the tee socket always succeeds */ 206 guac_mem_free(data); 207 return 0; 208 209} 210 211guac_socket* guac_socket_tee(guac_socket* primary, guac_socket* secondary) { 212 213 /* Set up socket to split outout into a file */ 214 guac_socket_tee_data* data = guac_mem_alloc(sizeof(guac_socket_tee_data)); 215 data->primary = primary; 216 data->secondary = secondary; 217 218 /* Associate tee-specific data with new socket */ 219 guac_socket* socket = guac_socket_alloc(); 220 socket->data = data; 221 222 /* Assign handlers */ 223 socket->read_handler = __guac_socket_tee_read_handler; 224 socket->write_handler = __guac_socket_tee_write_handler; 225 socket->select_handler = __guac_socket_tee_select_handler; 226 socket->flush_handler = __guac_socket_tee_flush_handler; 227 socket->lock_handler = __guac_socket_tee_lock_handler; 228 socket->unlock_handler = __guac_socket_tee_unlock_handler; 229 socket->free_handler = __guac_socket_tee_free_handler; 230 231 return socket; 232 233} 234