]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * Copyright (c) 2012 Apple Computer, Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include <sysexits.h> | |
25 | #include "timestampclient.h" | |
26 | #include <syslog.h> | |
27 | ||
28 | struct connection_info { | |
29 | xpc_connection_t peer; | |
30 | int processed; | |
31 | int done; | |
32 | }; | |
33 | ||
34 | xpc_object_t keychain_prefs_path = NULL; | |
35 | xpc_object_t home = NULL; | |
36 | ||
37 | extern xpc_object_t | |
38 | xpc_create_reply_with_format(xpc_object_t original, const char * format, ...); | |
39 | ||
40 | void finalize_connection(void *not_used); | |
41 | void handle_connection_event(const xpc_connection_t peer); | |
42 | void handle_request_event(struct connection_info *info, xpc_object_t event); | |
43 | ||
44 | #ifndef NDEBUG | |
45 | #define xpctsa_secdebug(format...) \ | |
46 | do { \ | |
47 | syslog(LOG_WARNING, format); \ | |
48 | } while (0) | |
49 | #define xpctsaNSLog(format...) \ | |
50 | do { \ | |
51 | NSLog(format); \ | |
52 | } while (0) | |
53 | ||
54 | #else | |
55 | //empty | |
56 | #define xpctsa_secdebug(format...) | |
57 | #define xpctsaNSLog(format...) | |
58 | #endif | |
59 | #define xpctsaDebug(args...) xpctsa_secdebug(args) | |
60 | ||
61 | /* | |
62 | These came from: | |
63 | ||
64 | #include <OSServices/NetworkUtilities.h> | |
65 | ||
66 | I have no idea why they aren't more accessible. | |
67 | */ | |
68 | ||
69 | #define kHTTPResponseCodeContinue 100 | |
70 | #define kHTTPResponseCodeOK 200 | |
71 | #define kHTTPResponseCodeNoContent 204 | |
72 | #define kHTTPResponseCodeBadRequest 400 | |
73 | #define kHTTPResponseCodeUnauthorized 401 | |
74 | #define kHTTPResponseCodeForbidden 403 | |
75 | #define kHTTPResponseCodeConflict 409 | |
76 | #define kHTTPResponseCodeExpectationFailed 417 | |
77 | #define kHTTPResponseCodeServFail 500 | |
78 | #define kHTTPResponseCodeInsufficientStorage 507 | |
79 | ||
80 | // | |
81 | // Turn a CFString into a UTF8-encoded C string. | |
82 | // | |
83 | static char *cfStringToCString(CFStringRef inStr) | |
84 | { | |
85 | if (!inStr) | |
86 | return ""; | |
87 | CFRetain(inStr); // compensate for release on exit | |
88 | ||
89 | // need to extract into buffer | |
90 | CFIndex length = CFStringGetLength(inStr); // in 16-bit character units | |
91 | size_t len = 6 * length + 1; | |
92 | char *buffer = malloc(len); // pessimistic | |
93 | if (!CFStringGetCString(inStr, buffer, len, kCFStringEncodingUTF8)) | |
94 | buffer[0]=0; | |
95 | ||
96 | CFRelease(inStr); | |
97 | return buffer; | |
98 | } | |
99 | ||
100 | static void debugShowTSAResponseInfo(NSURLResponse *response, NSData *data, NSError *err) | |
101 | { | |
102 | #ifndef NDEBUG | |
103 | if (response) | |
104 | { | |
105 | NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; | |
106 | NSInteger statusCode = [httpResponse statusCode]; | |
107 | NSDictionary *headers = [httpResponse allHeaderFields]; | |
108 | NSString *errStr2 = [NSHTTPURLResponse localizedStringForStatusCode:(NSInteger)statusCode]; | |
109 | ||
110 | xpctsaNSLog(@"TSA Response: %d, %@, %@", (int)statusCode, errStr2, headers); | |
111 | } | |
112 | ||
113 | if (err) | |
114 | { xpctsaNSLog(@"TSARequestCompletionBlock error: %@", err); } | |
115 | ||
116 | if (data) | |
117 | { | |
118 | xpctsaDebug("TSARequestCompletionBlock: Data (%lu bytes)",(unsigned long)[data length]); | |
119 | xpctsaNSLog(@"TSARequestCompletionBlock: Data (%lu bytes)",(unsigned long)[data length]); | |
120 | ||
121 | NSString *path = @"/tmp/tsaresp.rsp"; | |
122 | NSDataWritingOptions writeOptionsMask = 0; | |
123 | NSError *errorPtr = NULL; | |
124 | [data writeToFile:path options:writeOptionsMask error:&errorPtr]; | |
125 | if (errorPtr) | |
126 | { | |
127 | xpctsaNSLog(@"TSA Response error dumping response: %@", errorPtr); | |
128 | [errorPtr release]; | |
129 | } | |
130 | } | |
131 | #endif | |
132 | } | |
133 | ||
134 | static void communicateWithTimeStampingServer(xpc_object_t event, const char *requestData, size_t requestLength, const char *tsaURL) | |
135 | { | |
136 | if ((requestLength==0) || !tsaURL) | |
137 | return; | |
138 | ||
139 | xpctsaDebug("Request Length: %ld, URL: %s", requestLength, tsaURL); | |
140 | ||
141 | __block CFDataRef tsaReq = CFDataCreate(kCFAllocatorDefault, (const unsigned char *)requestData, requestLength); | |
142 | ||
143 | // The completion block is called when we have a response | |
144 | TSARequestCompletionBlock reqCompletionBlock = | |
145 | ^(NSURLResponse *response, NSData *data, NSError *err) | |
146 | { | |
147 | xpc_object_t tsaError = NULL; | |
148 | xpc_object_t tsaStatusCode = NULL; | |
149 | NSString *errStr = NULL; | |
150 | ||
151 | debugShowTSAResponseInfo(response, data, err); | |
152 | ||
153 | /* | |
154 | Handle errors first. The bad responses seen so far tend to | |
155 | return a bad HTTP status code rather than setting the "err" | |
156 | parameter. In this case, the "data" parameter contains the | |
157 | HTML of the error response, which we do not want to try to | |
158 | parse as ASN.1 data. | |
159 | */ | |
160 | ||
161 | if (response) | |
162 | { | |
163 | NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; | |
164 | NSInteger statusCode = [httpResponse statusCode]; | |
165 | if (statusCode != kHTTPResponseCodeOK) | |
166 | { | |
167 | errStr = [NSHTTPURLResponse localizedStringForStatusCode:(NSInteger)statusCode]; | |
168 | tsaStatusCode = xpc_int64_create((int64_t)statusCode); | |
169 | xpctsaNSLog(@"TSA Response-b: %d, %@", (int)statusCode, errStr); | |
170 | } | |
171 | } | |
172 | ||
173 | if (err && !errStr) | |
174 | { | |
175 | errStr = [err description]; | |
176 | if (!tsaStatusCode) | |
177 | { | |
178 | NSInteger statusCode = [err code]; | |
179 | tsaStatusCode = xpc_int64_create((int64_t)statusCode); | |
180 | } | |
181 | } | |
182 | ||
183 | if (errStr) | |
184 | { | |
185 | const char *cerrstr = cfStringToCString((CFStringRef)errStr); | |
186 | tsaError = xpc_string_create(cerrstr); | |
187 | xpctsaNSLog(@"TSA Response-c: %@", errStr); | |
188 | } | |
189 | ||
190 | size_t length = (errStr || !data) ? 0 : [data length]; | |
191 | xpc_object_t tsaReply = xpc_data_create([data bytes], length); | |
192 | xpc_connection_t peer = xpc_dictionary_get_remote_connection(event); | |
193 | xpc_object_t reply = (tsaError)? | |
194 | xpc_create_reply_with_format(event, | |
195 | "{TimeStampReply: %value, TimeStampError: %value, TimeStampStatus: %value}", tsaReply, tsaError, tsaStatusCode) : | |
196 | xpc_create_reply_with_format(event, "{TimeStampReply: %value}", tsaReply); | |
197 | xpc_connection_send_message(peer, reply); | |
198 | xpc_release(reply); | |
199 | ||
200 | if (tsaReq) | |
201 | CFRelease(tsaReq); | |
202 | if (tsaReply) | |
203 | xpc_release(tsaReply); | |
204 | if (tsaError) | |
205 | xpc_release(tsaError); | |
206 | if (tsaStatusCode) | |
207 | xpc_release(tsaStatusCode); | |
208 | }; | |
209 | ||
210 | sendTSARequest(tsaReq, tsaURL, reqCompletionBlock); | |
211 | } | |
212 | ||
213 | void handle_request_event(struct connection_info *info, xpc_object_t event) | |
214 | { | |
215 | xpc_connection_t peer = xpc_dictionary_get_remote_connection(event); | |
216 | xpc_type_t xtype = xpc_get_type(event); | |
217 | ||
218 | if (info->done) | |
219 | { | |
220 | xpctsaDebug("event %p while done", event); | |
221 | return; | |
222 | } | |
223 | if (xtype == XPC_TYPE_ERROR) | |
224 | { | |
225 | if (event == XPC_ERROR_TERMINATION_IMMINENT) { | |
226 | // launchd would like us to die, but we have open transactions. When we finish with them xpc_service_main | |
227 | // will exit for us, so there is nothing for us to do here. | |
228 | return; | |
229 | } | |
230 | ||
231 | if (!info->done) { | |
232 | info->done = true; | |
233 | xpc_release(info->peer); | |
234 | } | |
235 | if (peer == NULL && XPC_ERROR_CONNECTION_INVALID == event && 0 != info->processed) { | |
236 | // this is a normal shutdown on a connection that has processed at least | |
237 | // one request. Nothing intresting to log. | |
238 | return; | |
239 | } | |
240 | xpctsaDebug("listener event error (connection %p): %s", peer, xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); | |
241 | } | |
242 | else | |
243 | if (xtype == XPC_TYPE_DICTIONARY) | |
244 | { | |
245 | size_t length = 0; | |
246 | const char *operation = xpc_dictionary_get_string(event, "operation"); | |
247 | if (operation && !strcmp(operation, "TimeStampRequest")) | |
248 | { | |
249 | xpctsaDebug("Handling TimeStampRequest event"); | |
250 | const void *requestData = xpc_dictionary_get_data(event, "TimeStampRequest", &length); | |
251 | const char *url = xpc_dictionary_get_string(event, "ServerURL"); | |
252 | ||
253 | communicateWithTimeStampingServer(event, requestData, length, url); | |
254 | } | |
255 | else | |
256 | xpctsaDebug("Unknown op=%s request from pid %d", operation, xpc_connection_get_pid(peer)); | |
257 | } | |
258 | else | |
259 | xpctsaDebug("Unhandled request event=%p type=%p", event, xtype); | |
260 | } | |
261 | ||
262 | void finalize_connection(void *not_used) | |
263 | { | |
264 | xpc_transaction_end(); | |
265 | } | |
266 | ||
267 | void handle_connection_event(const xpc_connection_t peer) | |
268 | { | |
269 | __block struct connection_info info; | |
270 | info.peer = peer; | |
271 | info.processed = 0; | |
272 | info.done = false; | |
273 | ||
274 | xpc_connection_set_event_handler(peer, ^(xpc_object_t event) | |
275 | { | |
276 | handle_request_event(&info, event); | |
277 | }); | |
278 | ||
279 | // unlike dispatch objects xpc objects don't need a context set in order to run a finalizer. (we use our finalizer to | |
280 | // end the transaction we are about to begin...this keeps xpc from idle exiting us while we have a live connection) | |
281 | xpc_connection_set_finalizer_f(peer, finalize_connection); | |
282 | xpc_transaction_begin(); | |
283 | ||
284 | // enable the peer connection to receive messages | |
285 | xpc_connection_resume(peer); | |
286 | xpc_retain(peer); | |
287 | } | |
288 | ||
289 | int main(int argc, const char *argv[]) | |
290 | { | |
291 | char *wait4debugger = getenv("WAIT4DEBUGGER"); | |
292 | if (wait4debugger && !strcasecmp("YES", wait4debugger)) | |
293 | { | |
294 | syslog(LOG_ERR, "Waiting for debugger"); | |
295 | kill(getpid(), SIGSTOP); | |
296 | } | |
297 | ||
298 | xpc_main(handle_connection_event); | |
299 | ||
300 | return EX_OSERR; | |
301 | } | |
302 |