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