1 /*
2  * synergy -- mouse and keyboard sharing utility
3  * Copyright (C) 2012-2016 Symless Ltd.
4  * Copyright (C) 2012 Nick Bolton
5  *
6  * This package is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * found in the file LICENSE that should have accompanied this file.
9  *
10  * This package is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "ipc/IpcClientProxy.h"
20 
21 #include "ipc/Ipc.h"
22 #include "ipc/IpcMessage.h"
23 #include "synergy/ProtocolUtil.h"
24 #include "io/IStream.h"
25 #include "arch/Arch.h"
26 #include "base/TMethodEventJob.h"
27 #include "base/Log.h"
28 
29 //
30 // IpcClientProxy
31 //
32 
IpcClientProxy(synergy::IStream & stream,IEventQueue * events)33 IpcClientProxy::IpcClientProxy(synergy::IStream& stream, IEventQueue* events) :
34     m_stream(stream),
35     m_clientType(kIpcClientUnknown),
36     m_disconnecting(false),
37     m_readMutex(ARCH->newMutex()),
38     m_writeMutex(ARCH->newMutex()),
39     m_events(events)
40 {
41     m_events->adoptHandler(
42         m_events->forIStream().inputReady(), stream.getEventTarget(),
43         new TMethodEventJob<IpcClientProxy>(
44         this, &IpcClientProxy::handleData));
45 
46     m_events->adoptHandler(
47         m_events->forIStream().outputError(), stream.getEventTarget(),
48         new TMethodEventJob<IpcClientProxy>(
49         this, &IpcClientProxy::handleWriteError));
50 
51     m_events->adoptHandler(
52         m_events->forIStream().inputShutdown(), stream.getEventTarget(),
53         new TMethodEventJob<IpcClientProxy>(
54         this, &IpcClientProxy::handleDisconnect));
55 
56     m_events->adoptHandler(
57         m_events->forIStream().outputShutdown(), stream.getEventTarget(),
58         new TMethodEventJob<IpcClientProxy>(
59         this, &IpcClientProxy::handleWriteError));
60 }
61 
~IpcClientProxy()62 IpcClientProxy::~IpcClientProxy()
63 {
64     m_events->removeHandler(
65         m_events->forIStream().inputReady(), m_stream.getEventTarget());
66     m_events->removeHandler(
67         m_events->forIStream().outputError(), m_stream.getEventTarget());
68     m_events->removeHandler(
69         m_events->forIStream().inputShutdown(), m_stream.getEventTarget());
70     m_events->removeHandler(
71         m_events->forIStream().outputShutdown(), m_stream.getEventTarget());
72 
73     // don't delete the stream while it's being used.
74     ARCH->lockMutex(m_readMutex);
75     ARCH->lockMutex(m_writeMutex);
76     delete &m_stream;
77     ARCH->unlockMutex(m_readMutex);
78     ARCH->unlockMutex(m_writeMutex);
79 
80     ARCH->closeMutex(m_readMutex);
81     ARCH->closeMutex(m_writeMutex);
82 }
83 
84 void
handleDisconnect(const Event &,void *)85 IpcClientProxy::handleDisconnect(const Event&, void*)
86 {
87     disconnect();
88     LOG((CLOG_DEBUG "ipc client disconnected"));
89 }
90 
91 void
handleWriteError(const Event &,void *)92 IpcClientProxy::handleWriteError(const Event&, void*)
93 {
94     disconnect();
95     LOG((CLOG_DEBUG "ipc client write error"));
96 }
97 
98 void
handleData(const Event &,void *)99 IpcClientProxy::handleData(const Event&, void*)
100 {
101     // don't allow the dtor to destroy the stream while we're using it.
102     ArchMutexLock lock(m_readMutex);
103 
104     LOG((CLOG_DEBUG "start ipc handle data"));
105 
106     UInt8 code[4];
107     UInt32 n = m_stream.read(code, 4);
108     while (n != 0) {
109 
110         LOG((CLOG_DEBUG "ipc read: %c%c%c%c",
111             code[0], code[1], code[2], code[3]));
112 
113         IpcMessage* m = nullptr;
114         if (memcmp(code, kIpcMsgHello, 4) == 0) {
115             m = parseHello();
116         }
117         else if (memcmp(code, kIpcMsgCommand, 4) == 0) {
118             m = parseCommand();
119         }
120         else {
121             LOG((CLOG_ERR "invalid ipc message"));
122             disconnect();
123         }
124 
125         // don't delete with this event; the data is passed to a new event.
126         Event e(m_events->forIpcClientProxy().messageReceived(), this, NULL, Event::kDontFreeData);
127         e.setDataObject(m);
128         m_events->addEvent(e);
129 
130         n = m_stream.read(code, 4);
131     }
132 
133     LOG((CLOG_DEBUG "finished ipc handle data"));
134 }
135 
136 void
send(const IpcMessage & message)137 IpcClientProxy::send(const IpcMessage& message)
138 {
139     // don't allow other threads to write until we've finished the entire
140     // message. stream write is locked, but only for that single write.
141     // also, don't allow the dtor to destroy the stream while we're using it.
142     ArchMutexLock lock(m_writeMutex);
143 
144     LOG((CLOG_DEBUG4 "ipc write: %d", message.type()));
145 
146     switch (message.type()) {
147     case kIpcLogLine: {
148         const IpcLogLineMessage& llm = static_cast<const IpcLogLineMessage&>(message);
149         const String logLine = llm.logLine();
150         ProtocolUtil::writef(&m_stream, kIpcMsgLogLine, &logLine);
151         break;
152     }
153 
154     case kIpcShutdown:
155         ProtocolUtil::writef(&m_stream, kIpcMsgShutdown);
156         break;
157 
158     default:
159         LOG((CLOG_ERR "ipc message not supported: %d", message.type()));
160         break;
161     }
162 }
163 
164 IpcHelloMessage*
parseHello()165 IpcClientProxy::parseHello()
166 {
167     UInt8 type;
168     ProtocolUtil::readf(&m_stream, kIpcMsgHello + 4, &type);
169 
170     m_clientType = static_cast<EIpcClientType>(type);
171 
172     // must be deleted by event handler.
173     return new IpcHelloMessage(m_clientType);
174 }
175 
176 IpcCommandMessage*
parseCommand()177 IpcClientProxy::parseCommand()
178 {
179     String command;
180     UInt8 elevate;
181     ProtocolUtil::readf(&m_stream, kIpcMsgCommand + 4, &command, &elevate);
182 
183     // must be deleted by event handler.
184     return new IpcCommandMessage(command, elevate != 0);
185 }
186 
187 void
disconnect()188 IpcClientProxy::disconnect()
189 {
190     LOG((CLOG_DEBUG "ipc disconnect, closing stream"));
191     m_disconnecting = true;
192     m_stream.close();
193     m_events->addEvent(Event(m_events->forIpcClientProxy().disconnected(), this));
194 }
195