1/* 2 * MUSCLE SmartCard Development ( http://www.linuxnet.com ) 3 * 4 * Copyright (C) 2001-2004 5 * David Corcoran <corcoran@linuxnet.com> 6 * Damien Sauveron <damien.sauveron@labri.fr> 7 * Ludoic Rousseau <ludovic.rousseau@free.fr> 8 * 9 * $Id: winscard_msg_srv.c 123 2010-03-27 10:50:42Z ludovic.rousseau@gmail.com $ 10 */ 11 12/** 13 * @file 14 * @brief client/server communication (on the server side only) 15 * 16 * A file based socket (\c commonSocket) is used to send/receive only messages 17 * among clients and server.\n 18 * The messages' data are passed throw a memory mapped file: \c sharedSegmentMsg. 19 */ 20 21#include "config.h" 22#include <fcntl.h> 23#include <unistd.h> 24#include <sys/types.h> 25#include <sys/stat.h> 26#include <sys/socket.h> 27#include <sys/time.h> 28#include <sys/un.h> 29#include <sys/ioctl.h> 30#include <errno.h> 31#include <stdio.h> 32#include <time.h> 33#include <string.h> 34#ifdef HAVE_SYS_FILIO_H 35#include <sys/filio.h> 36#endif 37 38#include "wintypes.h" 39#include "pcscexport.h" 40#include "winscard.h" 41#include "debuglog.h" 42#include "winscard_msg.h" 43#include "sys_generic.h" 44 45/** 46 * Socket to a file, used for clients-server comminication. 47 */ 48static int commonSocket = 0; 49extern char AraKiri; 50extern char ReCheckSerialReaders; 51 52/** 53 * @brief Accepts a Client connection. 54 * 55 * Called by \c SHMProcessEventsServer(). 56 * 57 * @param[out] pdwClientID Connection ID used to reference the Client. 58 * 59 * @return Error code. 60 * @retval 0 Success. 61 * @retval -1 Can not establish the connection. 62 * @retval -1 Can not set the connection to non-blocking mode. 63 */ 64static int SHMProcessCommonChannelRequest(uint32_t *pdwClientID) 65{ 66 socklen_t clnt_len; 67 int new_sock; 68 struct sockaddr_un clnt_addr; 69 int one; 70 71 clnt_len = sizeof(clnt_addr); 72 73 if ((new_sock = accept(commonSocket, (struct sockaddr *) &clnt_addr, 74 &clnt_len)) < 0) 75 { 76 Log2(PCSC_LOG_CRITICAL, "Accept on common socket: %s", 77 strerror(errno)); 78 return -1; 79 } 80 81 *pdwClientID = new_sock; 82 83 one = 1; 84 if (ioctl(*pdwClientID, FIONBIO, &one) < 0) 85 { 86 Log2(PCSC_LOG_CRITICAL, "Error: cannot set socket nonblocking: %s", 87 strerror(errno)); 88 SYS_CloseFile(*pdwClientID); 89 *pdwClientID = -1; 90 return -1; 91 } 92 93 return 0; 94} 95 96/** 97 * @brief Prepares the communication channel used by the server to talk to the 98 * clients. 99 * 100 * This is called by the server to create a socket for local IPC with the 101 * clients. The socket is associated to the file \c PCSCLITE_CSOCK_NAME. 102 * Each client will open a connection to this socket. 103 * 104 * @return Error code. 105 * @retval 0 Success 106 * @retval -1 Can not create the socket. 107 * @retval -1 Can not bind the socket to the file \c PCSCLITE_CSOCK_NAME. 108 * @retval -1 Can not put the socket in listen mode. 109 */ 110INTERNAL int SHMInitializeCommonSegment(void) 111{ 112 static struct sockaddr_un serv_adr; 113 114 /* 115 * Create the common shared connection socket 116 */ 117 if ((commonSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) 118 { 119 Log2(PCSC_LOG_CRITICAL, "Unable to create common socket: %s", 120 strerror(errno)); 121 return -1; 122 } 123 124 serv_adr.sun_family = AF_UNIX; 125 strncpy(serv_adr.sun_path, PCSCLITE_CSOCK_NAME, 126 sizeof(serv_adr.sun_path)); 127 SYS_Unlink(PCSCLITE_CSOCK_NAME); 128 129 if (bind(commonSocket, (struct sockaddr *) &serv_adr, 130 sizeof(serv_adr.sun_family) + strlen(serv_adr.sun_path) + 1) < 0) 131 { 132 Log2(PCSC_LOG_CRITICAL, "Unable to bind common socket: %s", 133 strerror(errno)); 134 SHMCleanupSharedSegment(commonSocket, PCSCLITE_CSOCK_NAME); 135 return -1; 136 } 137 138 if (listen(commonSocket, 1) < 0) 139 { 140 Log2(PCSC_LOG_CRITICAL, "Unable to listen common socket: %s", 141 strerror(errno)); 142 SHMCleanupSharedSegment(commonSocket, PCSCLITE_CSOCK_NAME); 143 return -1; 144 } 145 146 /* 147 * Chmod the public entry channel 148 */ 149 SYS_Chmod(PCSCLITE_CSOCK_NAME, S_IRWXO | S_IRWXG | S_IRWXU); 150 151 return 0; 152} 153 154/** 155 * @brief Looks for messages sent by clients. 156 * 157 * This is called by the Server's function \c SVCServiceRunLoop(). 158 * 159 * @param[out] pdwClientID Connection ID used to reference the Client. 160 * @param[in] blocktime Timeout (not used). 161 * 162 * @return Error code. 163 * @retval 0 Success. 164 * @retval -1 Error accessing the communication channel. 165 * @retval -1 Can not set the connection to non-blocking mode. 166 * @retval 2 Timeout. 167 */ 168#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) 169#define DO_TIMEOUT 170#endif 171INTERNAL int32_t SHMProcessEventsServer(uint32_t *pdwClientID, int32_t blocktime) 172{ 173 fd_set read_fd; 174 int selret; 175#ifdef DO_TIMEOUT 176 struct timeval tv; 177 178 tv.tv_sec = 1; 179 tv.tv_usec = 0; 180#endif 181 182 FD_ZERO(&read_fd); 183 184 /* 185 * Set up the bit masks for select 186 */ 187 FD_SET(commonSocket, &read_fd); 188 189 selret = select(commonSocket + 1, &read_fd, (fd_set *) NULL, 190 (fd_set *) NULL, 191#ifdef DO_TIMEOUT 192 &tv 193#else 194 NULL 195#endif 196 ); 197 198 if (selret < 0) 199 { 200 if (EINTR == errno) 201 return -2; 202 203 Log2(PCSC_LOG_CRITICAL, "Select returns with failure: %s", 204 strerror(errno)); 205 return -1; 206 } 207 208 if (selret == 0) 209 /* timeout. On *BSD only */ 210 return 2; 211 212 /* 213 * A common pipe packet has arrived - it could be a new application 214 */ 215 if (FD_ISSET(commonSocket, &read_fd)) 216 { 217 Log1(PCSC_LOG_DEBUG, "Common channel packet arrival"); 218 if (SHMProcessCommonChannelRequest(pdwClientID) == -1) 219 { 220 Log2(PCSC_LOG_ERROR, 221 "error in SHMProcessCommonChannelRequest: %d", *pdwClientID); 222 return -1; 223 } else 224 { 225 Log2(PCSC_LOG_DEBUG, 226 "SHMProcessCommonChannelRequest detects: %d", *pdwClientID); 227 return 0; 228 } 229 } 230 231 return -1; 232} 233 234/** 235 * @brief 236 * 237 * Called by \c ContextThread(). 238 */ 239INTERNAL int32_t SHMProcessEventsContext(uint32_t dwClientID, 240 psharedSegmentMsg msgStruct, int32_t blocktime) 241{ 242 fd_set read_fd; 243 int selret, rv; 244 struct timeval tv; 245 246 tv.tv_sec = 1; 247 tv.tv_usec = 0; 248 249 FD_ZERO(&read_fd); 250 FD_SET(dwClientID, &read_fd); 251 252 selret = select(dwClientID + 1, &read_fd, (fd_set *) NULL, 253 (fd_set *) NULL, &tv); 254 255 if (selret < 0) 256 { 257 Log2(PCSC_LOG_ERROR, "select returns with failure: %s", 258 strerror(errno)); 259 return -1; 260 } 261 262 if (selret == 0) 263 { 264// Log3(PCSC_LOG_ERROR, "SHMProcessEventsContext: select timed out, errno: %d, %s", errno, 265// strerror(errno)); 266 /* timeout */ 267 return 2; 268 } 269 270 if (FD_ISSET(dwClientID, &read_fd)) 271 { 272 /* 273 * Return the current handle 274 */ 275 /* 276 The 0 for size below means that SHMClientReadMessage should calculate the size from the 277 message header. The size is not sizeof(*msgStruct) 278 */ 279 rv = SHMClientReadMessage(msgStruct, dwClientID, 0, SHMCommunicationTimeout()); 280 if (rv == -1) 281 { /* The client has died */ 282 Log2(PCSC_LOG_DEBUG, "Client has disappeared: %d", dwClientID); 283 msgStruct->mtype = CMD_CLIENT_DIED; 284 msgStruct->command = 0; 285 SYS_CloseFile(dwClientID); 286 287 return 0; 288 } 289 290 /* 291 * Set the identifier handle 292 */ 293 Log2(PCSC_LOG_DEBUG, "correctly processed client: %d", dwClientID); 294 return 1; 295 } 296 297 return -1; 298} 299 300INTERNAL int SHMCommunicationTimeout() 301{ 302 /* 303 This is a param to e.g. SHMClientReadMessage, and is a timeout in milliseconds. 304 The constant PCSCLITE_SERVER_ATTEMPTS is very poorly named; it is a time value 305 in milliseconds, not the number of attempts. Some values to use: 306 5 default if PCSCLITE_ENHANCED_MESSAGING not defined 307 200 if PCSCLITE_ENHANCED_MESSAGING is defined 308 12000 might be a good value to set while debugging 309 */ 310 311 static int baseTimeout = 12000;//PCSCLITE_SERVER_ATTEMPTS; 312 volatile int timeOut = baseTimeout; 313 314 return timeOut; 315} 316