2 * Copyright (c) 2012 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 #include "timestampclient.h"
28 struct connection_info {
29 xpc_connection_t peer;
34 xpc_object_t keychain_prefs_path = NULL;
35 xpc_object_t home = NULL;
38 xpc_create_reply_with_format(xpc_object_t original, const char * format, ...);
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);
45 #define xpctsa_secdebug(format...) \
47 syslog(LOG_WARNING, format); \
49 #define xpctsaNSLog(format...) \
56 #define xpctsa_secdebug(format...)
57 #define xpctsaNSLog(format...)
59 #define xpctsaDebug(args...) xpctsa_secdebug(args)
64 #include <OSServices/NetworkUtilities.h>
66 I have no idea why they aren't more accessible.
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
81 // Turn a CFString into a UTF8-encoded C string.
83 static char *cfStringToCString(CFStringRef inStr)
87 CFRetain(inStr); // compensate for release on exit
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))
100 static void debugShowTSAResponseInfo(NSURLResponse *response, NSData *data, NSError *err)
105 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
106 NSInteger statusCode = [httpResponse statusCode];
107 NSDictionary *headers = [httpResponse allHeaderFields];
108 NSString *errStr2 = [NSHTTPURLResponse localizedStringForStatusCode:(NSInteger)statusCode];
110 xpctsaNSLog(@"TSA Response: %d, %@, %@", (int)statusCode, errStr2, headers);
114 { xpctsaNSLog(@"TSARequestCompletionBlock error: %@", err); }
118 xpctsaDebug("TSARequestCompletionBlock: Data (%lu bytes)",(unsigned long)[data length]);
119 xpctsaNSLog(@"TSARequestCompletionBlock: Data (%lu bytes)",(unsigned long)[data length]);
121 NSString *path = @"/tmp/tsaresp.rsp";
122 NSDataWritingOptions writeOptionsMask = 0;
123 NSError *errorPtr = NULL;
124 [data writeToFile:path options:writeOptionsMask error:&errorPtr];
127 xpctsaNSLog(@"TSA Response error dumping response: %@", errorPtr);
134 static void communicateWithTimeStampingServer(xpc_object_t event, const char *requestData, size_t requestLength, const char *tsaURL)
136 if ((requestLength==0) || !tsaURL)
139 xpctsaDebug("Request Length: %ld, URL: %s", requestLength, tsaURL);
141 __block CFDataRef tsaReq = CFDataCreate(kCFAllocatorDefault, (const unsigned char *)requestData, requestLength);
143 // The completion block is called when we have a response
144 TSARequestCompletionBlock reqCompletionBlock =
145 ^(NSURLResponse *response, NSData *data, NSError *err)
147 xpc_object_t tsaError = NULL;
148 xpc_object_t tsaStatusCode = NULL;
149 NSString *errStr = NULL;
151 debugShowTSAResponseInfo(response, data, err);
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
163 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
164 NSInteger statusCode = [httpResponse statusCode];
165 if (statusCode != kHTTPResponseCodeOK)
167 errStr = [NSHTTPURLResponse localizedStringForStatusCode:(NSInteger)statusCode];
168 tsaStatusCode = xpc_int64_create((int64_t)statusCode);
169 xpctsaNSLog(@"TSA Response-b: %d, %@", (int)statusCode, errStr);
175 errStr = [err description];
178 NSInteger statusCode = [err code];
179 tsaStatusCode = xpc_int64_create((int64_t)statusCode);
185 const char *cerrstr = cfStringToCString((CFStringRef)errStr);
186 tsaError = xpc_string_create(cerrstr);
187 xpctsaNSLog(@"TSA Response-c: %@", errStr);
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);
203 xpc_release(tsaReply);
205 xpc_release(tsaError);
207 xpc_release(tsaStatusCode);
210 sendTSARequest(tsaReq, tsaURL, reqCompletionBlock);
213 void handle_request_event(struct connection_info *info, xpc_object_t event)
215 xpc_connection_t peer = xpc_dictionary_get_remote_connection(event);
216 xpc_type_t xtype = xpc_get_type(event);
220 xpctsaDebug("event %p while done", event);
223 if (xtype == XPC_TYPE_ERROR)
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.
233 xpc_release(info->peer);
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.
240 xpctsaDebug("listener event error (connection %p): %s", peer, xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
243 if (xtype == XPC_TYPE_DICTIONARY)
246 const char *operation = xpc_dictionary_get_string(event, "operation");
247 if (operation && !strcmp(operation, "TimeStampRequest"))
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");
253 communicateWithTimeStampingServer(event, requestData, length, url);
256 xpctsaDebug("Unknown op=%s request from pid %d", operation, xpc_connection_get_pid(peer));
259 xpctsaDebug("Unhandled request event=%p type=%p", event, xtype);
262 void finalize_connection(void *not_used)
264 xpc_transaction_end();
267 void handle_connection_event(const xpc_connection_t peer)
269 __block struct connection_info info;
274 xpc_connection_set_event_handler(peer, ^(xpc_object_t event)
276 handle_request_event(&info, event);
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();
284 // enable the peer connection to receive messages
285 xpc_connection_resume(peer);
289 int main(int argc, const char *argv[])
291 char *wait4debugger = getenv("WAIT4DEBUGGER");
292 if (wait4debugger && !strcasecmp("YES", wait4debugger))
294 syslog(LOG_ERR, "Waiting for debugger");
295 kill(getpid(), SIGSTOP);
298 xpc_main(handle_connection_event);