]> git.saurik.com Git - apple/security.git/blame - Network/xfercore.cpp
Security-176.tar.gz
[apple/security.git] / Network / xfercore.cpp
CommitLineData
bac41a7b
A
1/*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19//
20// xfercore - core data transfer engine
21//
22#include "xfercore.h"
23#include <Security/debugging.h>
24
25
26namespace Security {
27namespace Network {
28
29
30//
31// Create an engine-level client object.
32// @@@ Defer buffer allocation to mating?
33// @@@ Defer state initialization to mating?
34//
35TransferEngine::Client::Client()
36 : mMode(invalidInput), mAutoCopyOut(false),
37 mSink(NULL), mSource(NULL),
38 mAutoFlush(true),
39 mReadBuffer(16384), mWriteBuffer(16384)
40{
41}
42
43TransferEngine::Client::~Client()
44{
45}
46
47
48//
49// Add and remove clients to/from the engine
50//
51void TransferEngine::add(Client *client)
52{
53 client->io = client->fileDesc(); // punch master I/O down to Selector client level
54 Selector::add(client->io, *client, input | critical); // initial registration
55}
56
57void TransferEngine::remove(Client *client)
58{
59#ifndef NDEBUG
60 if (!client->mReadBuffer.isEmpty())
df0e469f 61 secdebug("xferengine", "xfer %p(%d) HAD %ld BYTES READ LEFT",
bac41a7b
A
62 client, client->fileDesc(), client->mReadBuffer.length());
63 if (!client->mWriteBuffer.isEmpty())
df0e469f 64 secdebug("xferengine", "xfer %p(%d) HAD %ld BYTES WRITE LEFT",
bac41a7b
A
65 client, client->fileDesc(), client->mWriteBuffer.length());
66#endif //NDEBUG
29654253
A
67 if (client->io.fd () != -1) { // did we have a live socket?
68 Selector::remove(client->io);
69 }
70
bac41a7b
A
71 client->io = FileDesc(); // invalidate
72}
73
74
75//
76// Mode switching.
77// In addition to the generic switcher (mode), there are variants that set associated
78// information, such as sources/sinks.
79//
80void TransferEngine::Client::mode(InputMode newMode)
81{
df0e469f 82 secdebug("xferengine", "xfer %p(%d) switching to mode %d", this, fileDesc(), newMode);
bac41a7b
A
83 switch (newMode) {
84 case rawInput:
85 case lineInput:
86 mMode = newMode;
87 break;
88 case connecting:
89 enable(output);
90 mMode = connecting;
91 break;
92 default:
93 assert(false); // can't switch to these modes like that
94 }
95}
96
97void TransferEngine::Client::mode(Sink &sink, size_t byteCount)
98{
99 mMode = autoReadInput;
100 mSink = &sink;
101 mResidualReadCount = byteCount;
df0e469f 102 secdebug("xferengine", "xfer %p(%d) switching to autoReadInput (%ld bytes)",
bac41a7b
A
103 this, fileDesc(), byteCount);
104}
105
106void TransferEngine::Client::mode(Source &source, size_t byteCount)
107{
108 assert (!mAutoCopyOut); // no replacements, please
109 mAutoCopyOut = true;
110 mSource = &source;
111 mResidualWriteCount = byteCount;
df0e469f 112 secdebug("xferengine", "xfer %p(%d) enabling autoCopyOut mode (%ld bytes)",
bac41a7b
A
113 this, fileDesc(), byteCount);
114 enable(output);
115}
116
117
118//
119// Output methods. This queues output to be sent to the client's connection
120// as soon as practical.
121//
122void TransferEngine::Client::printf(const char *format, ...)
123{
124 va_list args;
125 va_start(args, format);
126 vprintf(format, args);
127 va_end(args);
128}
129
130void TransferEngine::Client::vprintf(const char *format, va_list args)
131{
132 mWriteBuffer.vprintf(format, args);
133#if !defined(NDEBUG)
134 char buffer[1024];
135 vsnprintf(buffer, sizeof(buffer), format, args);
df0e469f 136 secdebug("engineio", "%p(%d) <-- %s", this, fileDesc(), buffer);
bac41a7b
A
137#endif //NDEBUG
138 startOutput();
139}
140
141void TransferEngine::Client::printfe(const char *format, ...)
142{
143 va_list args;
144 va_start(args, format);
145 vprintfe(format, args);
146 va_end(args);
147}
148
149void TransferEngine::Client::vprintfe(const char *format, va_list args)
150{
151 mWriteBuffer.vprintf(format, args);
152 mWriteBuffer.printf("\r\n");
153#if !defined(NDEBUG)
154 char buffer[1024];
155 vsnprintf(buffer, sizeof(buffer), format, args);
df0e469f 156 secdebug("engineio", "%p(%d) <-- %s[CRNL]", this, fileDesc(), buffer);
bac41a7b
A
157#endif //NDEBUG
158 startOutput();
159}
160
161
162//
163// Set output auto-flush mode. Think of this as a weak output-hold mode.
164// If autoflush is off, we don't try hard to send data out immediately. If it's
165// on, we send data as soon as it's generated.
166// Calling flushOutput(true) always generates I/O as needed to send output
167// data NOW (even if the mode was already on).
168//
169void TransferEngine::Client::flushOutput(bool autoFlush)
170{
171 mAutoFlush = autoFlush;
df0e469f 172 secdebug("engineio", "%p(%d) output flush %s", this, fileDesc(), autoFlush? "on" : "off");
bac41a7b
A
173 if (mAutoFlush)
174 startOutput();
175}
176
177
178//
179// StartOutput is called by output generators to get output flowing.
180// It may generate output I/O, or hold things in buffers according to
181// current settings.
182//
183void TransferEngine::Client::startOutput()
184{
185 if (mAutoFlush) {
186 if (mAutoCopyOut && !mWriteBuffer.isFull())
187 autoCopy(); // try to tack on some autoCopy output
188 if (!mWriteBuffer.isEmpty()) {
189 mWriteBuffer.write(*this);
190 if (mAutoFlush || !mWriteBuffer.isEmpty()) { // possibly more output
191 enable(output); // ask for output-drain notification
192 } else {
193 disable(output); // no need for output-possible events
194 }
195 }
196 }
197}
198
199
ded8f8e2
A
200//
201// Discard any data still in the input buffer.
202// This is used to cope with unexpected garbage (protocol violations
203// from the server), and shouldn't be used indiscriminately.
204//
205void TransferEngine::Client::flushInput()
206{
207 if (!mReadBuffer.isEmpty()) {
df0e469f 208 secdebug("engineio", "flushing %ld bytes of input", mReadBuffer.length());
ded8f8e2
A
209 mReadBuffer.clear();
210 mInputFlushed = true; // inhibit normal buffer ops
211 }
212}
213
214
bac41a7b
A
215//
216// Given that autoCopyOut mode is active, try to transfer some bytes
217// into the write buffer. This is a lazy, fast push, suitable for tacking on
218// when you are about to send data for some other reason.
219// Returns the number of bytes retrieved from the auto-Source (possibly zero).
220//
221size_t TransferEngine::Client::autoCopy()
222{
223 size_t len = mWriteBuffer.available(); //@@@ (true) ?
224 if (mResidualWriteCount && mResidualWriteCount < len)
225 len = mResidualWriteCount;
226 void *addr; mWriteBuffer.locatePut(addr, len);
227 mSource->produce(addr, len);
df0e469f 228 secdebug("xferengine", "xfer %p(%d) autoCopyOut source delivered %ld bytes",
bac41a7b
A
229 this, fileDesc(), len);
230 mWriteBuffer.usePut(len);
231 return len;
232}
233
234
235//
236// This is the notify function called by the IP Selector layer when I/O is possible.
237// It runs the state machines for all current clients, calling their transit methods
238// in turn.
239//
240void TransferEngine::Client::notify(int fd, Type type)
241{
242 try {
243 //@@@ Note: We do not currently do anything special about critical events.
244
245 if (type & Selector::output) {
246 // if we're in connecting mode
247 if (mMode == connecting) {
248 Socket s; s = fd; // Socket(fd) means something different...
249 int error = s.error();
df0e469f 250 secdebug("xferengine", "xfer %p(%d) connect (errno %d)",
bac41a7b
A
251 this, fd, error);
252 transit(connectionDone, NULL, error);
253 return;
254 }
255
256 //@@@ use high/low water marks here
257 if (mAutoCopyOut && !mWriteBuffer.isFull()) {
258 if (autoCopy() == 0) {
259 switch (mSource->state()) {
260 case Source::stalled:
261 // ah well, maybe later
df0e469f 262 secdebug("xferengine", "xfer %p(%d) autoCopyOut source is stalled", this, fd);
bac41a7b
A
263 break;
264 case Source::endOfData:
265 mAutoCopyOut = false; // done
df0e469f 266 secdebug("xferengine", "xfer %p(%d) autoCopyOut end of data", this, fd);
bac41a7b 267 if (mResidualWriteCount > 0)
df0e469f 268 secdebug("xferengine", "xfer %p(%d) has %ld autoCopy bytes left",
bac41a7b
A
269 this, fd, mResidualWriteCount);
270 transit(autoWriteDone);
271 if (!isActive())
272 return; // transit removed us; stop now
273 break;
274 default:
275 assert(false);
276 }
277 }
278 }
279 if (mWriteBuffer.isEmpty()) { // output possible, no output pending
df0e469f 280 secdebug("xferengine", "xfer %p(%d) disabling output (empty)", this, fd);
bac41a7b
A
281 disable(output);
282 } else { // stuff some more
283 size_t length = mWriteBuffer.write(*this);
df0e469f 284 secdebug("xferengine", "xfer %p(%d) writing %ld bytes", this, fd, length);
bac41a7b
A
285 }
286 }
287
288 if (type & Selector::input) {
df0e469f
A
289 secdebug("xferengine", "xfer %p(%d) input ready %d bytes",
290 this, fd, io.iocget<int>(FIONREAD));
bac41a7b
A
291
292 do {
ded8f8e2
A
293 mInputFlushed = false; // preset normal
294
bac41a7b 295 //@@@ break out after partial buffer to give Equal Time to other transfers? good idea?!
ded8f8e2 296 if (!atEnd() && mReadBuffer.read(*this) == 0 && !atEnd()) {
bac41a7b
A
297 mReadBuffer.read(*this, true);
298 }
299
300 if (mReadBuffer.isEmpty() && atEnd()) {
301 transit(endOfInput);
302 break;
303 }
304 switch (mMode) {
305 case rawInput:
306 rawInputTransit();
307 break;
308 case lineInput:
ded8f8e2
A
309 if (!lineInputTransit())
310 return; // no full line; try again later
bac41a7b
A
311 break;
312 case autoReadInput:
313 autoReadInputTransit();
314 if (mMode != autoIODone)
315 break;
316 // autoRead completed; fall through to autoIODone handling
317 case autoIODone:
318 mMode = invalidInput; // pre-mark error
319 transit(autoReadDone); // notify; this must reset mode or exit
320 if (!isActive()) // if we're terminated...
321 return; // ... then go
322 assert(mMode != invalidInput); // else enforce mode reset
323 break;
324 case connecting:
325 {
326 // we should never be here. Selector gave us "read but not write" while connecting. FUBAR
327 Socket s; s = fd;
df0e469f 328 secdebug("xferengine",
bac41a7b
A
329 "fd %d input while connecting (errno=%d, type=%d)",
330 fd, s.error(), type);
331 UnixError::throwMe(ECONNREFUSED); // likely interpretation
332 }
333 default:
df0e469f 334 secdebug("xferengine", "mode error in input sequencer (mode=%d)", mMode);
bac41a7b
A
335 assert(false);
336 }
337 if (!io) // client has unhooked; clear buffer and exit loop
ded8f8e2 338 flushInput();
bac41a7b
A
339 } while (!mReadBuffer.isEmpty());
340 //@@@ feed back for more output here? But also see comments above...
341 //@@@ probably better to take the trip through the Selector
342 }
29654253 343 } catch (const CssmCommonError &err) {
bac41a7b
A
344 transitError(err);
345 } catch (...) {
346 transitError(UnixError::make(EIO)); // best guess (could be anything)
347 }
348}
349
350void TransferEngine::Client::rawInputTransit()
351{
352 // just shove it at the user
353 char *addr; size_t length = mReadBuffer.length();
354 mReadBuffer.locateGet(addr, length);
df0e469f
A
355 secdebug("engineio", "%p(%d) --> %d bytes RAW",
356 this, fileDesc(), io.iocget<int>(FIONREAD));
bac41a7b 357 transit(inputAvailable, addr, length);
ded8f8e2
A
358 if (!mInputFlushed)
359 mReadBuffer.useGet(length);
bac41a7b
A
360}
361
ded8f8e2 362bool TransferEngine::Client::lineInputTransit()
bac41a7b
A
363{
364 char *line; size_t length = mReadBuffer.length();
365 mReadBuffer.locateGet(line, length);
366
367 char *nl;
368 for (nl = line; nl < line + length && *nl != '\n'; nl++) ;
369 if (nl == line + length) // no end-of-line, wait for more
ded8f8e2 370 return false;
bac41a7b
A
371
372 if (nl > line && nl[-1] == '\r') { // proper \r\n termination
373 nl[-1] = '\0'; // terminate for transit convenience
df0e469f 374 secdebug("engineio", "%p(%d) --> %s", this, fileDesc(), line);
bac41a7b
A
375 transit(inputAvailable, line, nl - line - 1);
376 } else { // improper, tolerate
377 nl[0] = '\0'; // terminate for transit convenience
df0e469f 378 secdebug("engineio", "%p(%d) [IMPROPER] --> %s", this, fileDesc(), line);
bac41a7b
A
379 transit(inputAvailable, line, nl - line);
380 }
ded8f8e2
A
381 if (!mInputFlushed)
382 mReadBuffer.useGet(nl - line + 1);
383 return true;
bac41a7b
A
384}
385
386void TransferEngine::Client::autoReadInputTransit()
387{
df0e469f 388 secdebug("xferengine", "xfer %p(%d) %ld pending %d available",
bac41a7b
A
389 this, fileDesc(), mReadBuffer.length(), io.iocget<int>(FIONREAD));
390 void *data; size_t length = mReadBuffer.length();
391 if (mResidualReadCount && mResidualReadCount < length)
392 length = mResidualReadCount;
393 mReadBuffer.locateGet(data, length);
df0e469f 394 secdebug("engineio", "%p(%d) --> %ld bytes autoReadInput", this, fileDesc(), length);
bac41a7b 395 mSink->consume(data, length);
ded8f8e2
A
396 if (!mInputFlushed)
397 mReadBuffer.useGet(length);
bac41a7b
A
398 if (mResidualReadCount && (mResidualReadCount -= length) == 0)
399 mMode = autoIODone;
400}
401
402
403//
404// The (protected) tickle() method causes a one-time scan
405// of the requesting client. This will simulate an input-ready event
406// and possibly call the transit method.
407// This is designed to be used from validate() or in other unusual
408// external situations. Don't call this from within transit().
409//
410void TransferEngine::Client::tickle()
411{
412 notify(io, input | critical);
413}
414
415
416//
417// The default read/write methods perform direct I/O on the underlying file descriptor.
418//
419size_t TransferEngine::Client::read(void *data, size_t size)
420{ return io.read(data, size); }
421
422size_t TransferEngine::Client::write(const void *data, size_t size)
423{ return io.write(data, size); }
424
425bool TransferEngine::Client::atEnd() const
426{ return io.atEnd(); }
427
428
429} // end namespace Network
430} // end namespace Security