Line data Source code
1 0 : /*
2 :
3 : OOTCPStreamDecoder.c
4 :
5 :
6 : Copyright (C) 2007 Jens Ayton and contributors
7 :
8 : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : of this software and associated documentation files (the "Software"), to deal
10 : in the Software without restriction, including without limitation the rights
11 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : copies of the Software, and to permit persons to whom the Software is
13 : furnished to do so, subject to the following conditions:
14 :
15 : The above copyright notice and this permission notice shall be included in all
16 : copies or substantial portions of the Software.
17 :
18 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : SOFTWARE.
25 :
26 : */
27 :
28 : #ifndef OO_EXCLUDE_DEBUG_SUPPORT
29 :
30 :
31 : #include "OOTCPStreamDecoder.h"
32 : #include <stdlib.h>
33 : #include <stdarg.h>
34 : #include <stdint.h>
35 : #include "OODebugTCPConsoleProtocol.h"
36 :
37 :
38 : #ifdef OO_LOG_DEBUG_PROTOCOL_PACKETS
39 : extern void LogOOTCPStreamDecoderPacket(OOALDictionaryRef packet);
40 : #else
41 0 : #define LogOOTCPStreamDecoderPacket(packet) do {} while (0)
42 : #endif
43 :
44 :
45 0 : struct OOTCPStreamDecoder
46 : {
47 0 : uint8_t header[4];
48 0 : uint32_t headerSpaceUsed;
49 0 : OOALMutableDataRef nextPacketData;
50 0 : uint32_t nextSize;
51 :
52 0 : OOTCPStreamDecoderPacketCallback Packet;
53 0 : OOTCPStreamDecoderErrorCallback Error;
54 0 : OOTCPStreamDecoderFinalizeCallback Finalize;
55 :
56 0 : void *cbInfo;
57 : };
58 :
59 :
60 : static void Error(OOTCPStreamDecoderRef decoder, OOALStringRef format, ...);
61 : static void PacketReady(OOTCPStreamDecoderRef decoder);
62 :
63 :
64 0 : OOTCPStreamDecoderRef OOTCPStreamDecoderCreate(OOTCPStreamDecoderPacketCallback packetCB, OOTCPStreamDecoderErrorCallback errorCB, OOTCPStreamDecoderFinalizeCallback finalizeCB, void *cbInfo)
65 : {
66 : OOTCPStreamDecoderRef decoder = NULL;
67 :
68 : if (packetCB == NULL) return NULL;
69 :
70 : decoder = malloc(sizeof *decoder);
71 : if (decoder == NULL) return NULL;
72 :
73 : decoder->headerSpaceUsed = 0;
74 : decoder->nextPacketData = NULL;
75 : decoder->nextSize = 0;
76 : decoder->Packet = packetCB;
77 : decoder->Error = errorCB;
78 : decoder->Finalize = finalizeCB;
79 : decoder->cbInfo = cbInfo;
80 :
81 : return decoder;
82 : }
83 :
84 :
85 0 : void OOTCPStreamDecoderDestroy(OOTCPStreamDecoderRef decoder)
86 : {
87 : if (decoder == NULL) return;
88 :
89 : if (decoder->Finalize != NULL)
90 : {
91 : decoder->Finalize(decoder->cbInfo);
92 : }
93 :
94 : if (decoder->nextPacketData != NULL)
95 : {
96 : OOALRelease(decoder->nextPacketData);
97 : decoder->nextPacketData = NULL;
98 : }
99 :
100 : free(decoder);
101 : }
102 :
103 :
104 0 : void OOTCPStreamDecoderReceiveData(OOTCPStreamDecoderRef decoder, OOALDataRef data)
105 : {
106 : if (decoder == NULL || data == NULL) return;
107 :
108 : OOTCPStreamDecoderReceiveBytes(decoder, OOALDataGetBytePtr(data), OOALDataGetLength(data));
109 : }
110 :
111 :
112 0 : void OOTCPStreamDecoderReceiveBytes(OOTCPStreamDecoderRef decoder, const void *inBytes, size_t length)
113 : {
114 : const unsigned char *bytes = NULL;
115 : size_t remaining;
116 : size_t bytesToAdd;
117 : OOALAutoreleasePoolRef pool = NULL;
118 :
119 : if (decoder == NULL) return;
120 :
121 : bytes = inBytes;
122 : remaining = length;
123 :
124 : if (bytes == NULL && remaining != 0)
125 : {
126 : Error(decoder, OOALSTR("Invalid data -- NULL bytes but %u byte count."), remaining);
127 : return;
128 : }
129 :
130 : while (remaining != 0)
131 : {
132 : if (decoder->nextPacketData != NULL)
133 : {
134 : // More data expected
135 : bytesToAdd = remaining;
136 : if (decoder->nextSize < bytesToAdd) bytesToAdd = decoder->nextSize;
137 :
138 : OOALMutableDataAppendBytes(decoder->nextPacketData, bytes, bytesToAdd);
139 :
140 : remaining -= bytesToAdd;
141 : decoder->nextSize -= bytesToAdd;
142 : bytes += bytesToAdd;
143 :
144 : if (decoder->nextSize == 0)
145 : {
146 : // Packet is ready.
147 : pool = OOALCreateAutoreleasePool();
148 : PacketReady(decoder);
149 : OOALDestroyAutoreleasePool(pool);
150 : pool = NULL;
151 :
152 : OOALRelease(decoder->nextPacketData);
153 : decoder->nextPacketData = NULL;
154 : }
155 : }
156 : else if (decoder->headerSpaceUsed < 4)
157 : {
158 : // Read bytes for packet header
159 : remaining--;
160 : decoder->header[decoder->headerSpaceUsed++] = *bytes++;
161 : }
162 : else if (decoder->headerSpaceUsed == 4)
163 : {
164 : // We've read a header, start on a packet.
165 : decoder->nextSize = (decoder->header[0] << 24) |
166 : (decoder->header[1] << 16) |
167 : (decoder->header[2] << 8) |
168 : (decoder->header[3] << 0);
169 :
170 : decoder->headerSpaceUsed = 0;
171 : if (decoder->nextSize != 0)
172 : {
173 : decoder->nextPacketData = OOALDataCreateMutable(decoder->nextSize);
174 : }
175 : }
176 : else
177 : {
178 : Error(decoder, OOALSTR("OOTCPStreamDecoder internal error: reached unreachable state. nextSize = %lu, bufferUsed = %lu, nextPacketData = %@."), (unsigned long)decoder->nextSize, (unsigned long)decoder->headerSpaceUsed, decoder->nextPacketData);
179 : }
180 : }
181 : }
182 :
183 :
184 0 : static void PacketReady(OOTCPStreamDecoderRef decoder)
185 : {
186 : OOALDictionaryRef packet = NULL;
187 : OOALStringRef errorString = NULL;
188 : OOALStringRef packetType = NULL;
189 :
190 : packet = OOALPropertyListFromData(decoder->nextPacketData, &errorString);
191 :
192 : // Ensure that it's a property list.
193 : if (packet == NULL)
194 : {
195 : Error(decoder, OOALSTR("Protocol error: packet is not property list (property list error: %@)."), errorString);
196 : OOALRelease(errorString);
197 : return;
198 : }
199 :
200 : // Ensure that it's a dictionary.
201 : if (!OOALIsDictionary(packet))
202 : {
203 : Error(decoder, OOALSTR("Protocol error: packet is a %@, not a dictionary."), OOTypeDescription(packet));
204 : return;
205 : }
206 :
207 : LogOOTCPStreamDecoderPacket(packet);
208 :
209 : // Get packet type (and ensure that there is one).
210 : packetType = OOALDictionaryGetValue(packet, kOOTCPPacketType);
211 : if (packetType == NULL)
212 : {
213 : Error(decoder, OOALSTR("Protocol error: packet contains no packet type."));
214 : return;
215 : }
216 :
217 : if (!OOALIsString(packetType))
218 : {
219 : Error(decoder, OOALSTR("Protocol error: packet type is a %@, not a string."), OOTypeDescription(packetType));
220 : return;
221 : }
222 :
223 : decoder->Packet(decoder->cbInfo, packetType, packet);
224 : }
225 :
226 :
227 0 : static void Error(OOTCPStreamDecoderRef decoder, OOALStringRef format, ...)
228 : {
229 : va_list args;
230 : OOALStringRef string = NULL;
231 :
232 : if (decoder == NULL || decoder->Error == NULL || format == NULL) return;
233 :
234 : va_start(args, format);
235 : string = OOALStringCreateWithFormatAndArguments(format, args);
236 : va_end(args);
237 :
238 : if (string != NULL)
239 : {
240 : decoder->Error(decoder->cbInfo, string);
241 : OOALRelease(string);
242 : }
243 : }
244 :
245 : #endif /* OO_EXCLUDE_DEBUG_SUPPORT */
|