示例#1
0
//
// Perform any preambles required to be a securityd client in good standing.
// This includes initial setup calls, thread registration, fork management,
// and (Code Signing) guest status.
//
void ClientSession::activate()
{
	// Guard against fork-without-exec. If we are the child of a fork
	// (that has not exec'ed), our apparent connection to SecurityServer
	// is just a mirage, and we better reset it.
	if (mHasForked()) {
		secinfo("SSclnt", "process has forked (now pid=%d) - resetting connection object", getpid());
		mGlobal.reset();
	}
		
	// now pick up the (new or existing) connection state
	Global &global = mGlobal();
    Thread &thread = global.thread();
    if (!thread) {
		// first time for this thread - use abbreviated registration
		IPCN(ucsp_client_setupThread(UCSP_ARGS, mach_task_self()));
        thread.registered = true;
        secinfo("SSclnt", "Thread registered with %s", mContactName);
	}
	
	// if the thread's guest state has changed, tell securityd
	if (thread.currentGuest != thread.lastGuest) {
		IPCN(ucsp_client_setGuest(UCSP_ARGS, thread.currentGuest, kSecCSDefaultFlags));
		thread.lastGuest = thread.currentGuest;
		secinfo("SSclnt", "switched guest state to 0x%x", thread.currentGuest);
	}
}
void PCSCMonitor::loadSoftToken(Bundle *tokendBundle)
{
	try {
		string bundleName = tokendBundle->identifier();
		
		// prepare a virtual reader, removing any existing one (this would kill a previous tokend)
		assert(mReaders.find(bundleName) == mReaders.end());	// not already present
		RefPointer<Reader> reader = new Reader(tokenCache(), bundleName);

		// now launch the tokend
		RefPointer<TokenDaemon> tokend = new TokenDaemon(tokendBundle,
			reader->name(), reader->pcscState(), reader->cache);
		
		if (tokend->state() == ServerChild::dead) {	// ah well, this one's no good
			secinfo("pcsc", "softtoken %s tokend launch failed", bundleName.c_str());
			Syslog::notice("Software token %s failed to run", tokendBundle->canonicalPath().c_str());
			return;
		}
		
		// probe the (single) tokend
		if (!tokend->probe()) {		// non comprende...
			secinfo("pcsc", "softtoken %s probe failed", bundleName.c_str());
			Syslog::notice("Software token %s refused operation", tokendBundle->canonicalPath().c_str());
			return;
		}
		
		// okay, this seems to work. Set it up
		mReaders.insert(make_pair(reader->name(), reader));
		reader->insertToken(tokend);
		Syslog::notice("Software token %s activated", bundleName.c_str());
	} catch (...) {
		secinfo("pcsc", "exception loading softtoken %s - continuing", tokendBundle->identifier().c_str());
	}
}
PathList::PathList(const string &subPath,
		const char *suffix /* = NULL */,
		const char *envar /* = NULL */,
		bool forUser /* = true */)
	: mSuffix(suffix)
{
	if (envar)
		if (const char *envPath = getenv(envar)) {
#if !defined(NDEBUG)
			if (envPath[0] == '!') {
				// envar="!path" -> single-item override (debugging only)
				mDebugOverride = envPath + 1;
				secinfo("pathlist", "%p env(\"%s\") overrides to \"%s\"",
					this, envar, mDebugOverride.c_str());
				return;
			}
#endif //NDEBUG

			// treat envPath as a classic colon-separated list of directories
			secinfo("pathlist", "%p configuring from env(\"%s\")", this, envar);
			while (const char *p = strchr(envPath, ':')) {
				addDirectory(string(envPath, p - envPath));
				envPath = p + 1;
			}
			addDirectory(envPath);
			return;
		}

	// no joy from environment variables
	secinfo("pathlist", "%p configuring from default path set \"%s\"", this, subPath.c_str());
	if (forUser)
		secinfo("pathlist", "user search list not yet implemented");
	addDirectory("/Library/" + subPath);
	addDirectory("/System/Library/" + subPath);
}
示例#4
0
//
// Notify an (interested) caller that a securityd-mediated ACL change
// MAY have happened on a key object involved in an operation. This allows
// such callers to re-encode key blobs for storage.
//
void ClientSession::notifyAclChange(KeyHandle key, CSSM_ACL_AUTHORIZATION_TAG tag)
{
	if (mCallback) {
		secinfo("keyacl", "ACL change key %u operation %u", key, tag);
		mCallback(mCallbackContext, *this, key, tag);
	} else
		secinfo("keyacl", "dropped ACL change notice for key %u operation %u",
			key, tag);
}
示例#5
0
//
// Common utility for finding the registered securityd port for the current
// session. This does not cache the port anywhere, though it does effectively
// cache the name.
//
Port ClientSession::findSecurityd()
{
	if (!mContactName)
	{
		mContactName = getenv(SECURITYSERVER_BOOTSTRAP_ENV);
		if (!mContactName)
			mContactName = SECURITYSERVER_BOOTSTRAP_NAME;
	}

    secinfo("SSclnt", "Locating %s", mContactName);
    Port serverPort = Bootstrap().lookup2(mContactName);
	secinfo("SSclnt", "contacting %s at port %d (version %d)",
		mContactName, serverPort.port(), SSPROTOVERSION);
	return serverPort;
}
//
// Remove some types of readers
//
void PCSCMonitor::clearReaders(Reader::Type type)
{
    if (!mReaders.empty()) {
        secinfo("pcsc", "%ld readers present - clearing type %d", mReaders.size(), type);
        for (ReaderMap::iterator it = mReaders.begin(); it != mReaders.end(); ) {
            ReaderMap::iterator cur = it++;
            Reader *reader = cur->second;
            if (reader->isType(type)) {
                secinfo("pcsc", "removing reader %s", reader->name().c_str());
                reader->kill();						// prepare to die
                mReaders.erase(cur);
            }
        }
    }
}
//
// Timer action. Perform the initial PCSC subsystem initialization.
// This runs (shortly) after securityd is fully functional and the
// server loop has started.
//
void PCSCMonitor::action()
{
    switch (mServiceLevel) {
        case forcedOff:
            secinfo("pcsc", "smartcard operation is FORCED OFF");
            break;

        case externalDaemon:
            secinfo("pcsc", "using PCSC");
            startSoftTokens();

            // Start PCSC reader watching thread.
            (new Watcher(server, tokenCache(), mReaders))->run();
            break;
    }
}
示例#8
0
//
// ClearReferences clears the reference set but does not propagate
// anything; it is NOT recursive.
//
void NodeCore::clearReferences()
{
	StLock<Mutex> _(*this);
	secinfo("ssnode", "%p clearing all %d references",
		this, int(mReferences.size()));
	mReferences.erase(mReferences.begin(), mReferences.end());
}
示例#9
0
//
// "Load" a plugin, given its MDS path. At this layer, we are performing
// a purely physical load operation. No code in the plugin is called.
// If "built-in plugins" are enabled, the moduleTable will come pre-initialized
// with certain paths. Since we consult this table before going to disk, this
// means that we'll pick these up first *as long as the paths match exactly*.
// There is nothing magical in the path strings themselves, other than by
// convention. (The convention is "*NAME", which conveniently does not match
// any actual file path.)
//
Plugin *ModuleLoader::operator () (const string &path)
{
    Plugin * &plugin = mPlugins[path];
    if (!plugin) {
		secinfo("cssm", "ModuleLoader(): creating plugin %s", path.c_str());
        plugin = new LoadablePlugin(path.c_str());
	}
	else {
		secinfo("cssm", "ModuleLoader(): FOUND plugin %s, isLoaded %s", 
			path.c_str(), plugin->isLoaded() ? "TRUE" : "FALSE");
		if(!plugin->isLoaded()) {
			plugin->load();
		}
	}
    return plugin;
}
void SharedMemoryListener::action ()
{
	secinfo("notify", "Posted notification to clients.");
    secdebug("MDSPRIVACY","[%03d] Posted notification to clients", mUID);
	notify_post (mSegmentName.c_str ());
	mActive = false;
}
示例#11
0
static int nfs_negotiate_security(const struct dentry *parent,
				  const struct dentry *dentry,
				  rpc_authflavor_t *flavor)
{
	struct page *page;
	struct nfs4_secinfo_flavors *flavors;
	int (*secinfo)(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
	int ret = -EPERM;

	secinfo = NFS_PROTO(parent->d_inode)->secinfo;
	if (secinfo != NULL) {
		page = alloc_page(GFP_KERNEL);
		if (!page) {
			ret = -ENOMEM;
			goto out;
		}
		flavors = page_address(page);
		ret = secinfo(parent->d_inode, &dentry->d_name, flavors);
		*flavor = nfs_find_best_sec(flavors);
		put_page(page);
	}

out:
	return ret;
}
示例#12
0
//
// Construct a Process object.
//
Process::Process(TaskPort taskPort,	const ClientSetupInfo *info, const CommonCriteria::AuditToken &audit)
 :  mTaskPort(taskPort), mByteFlipped(false), mPid(mTaskPort.pid()), mUid(0), mGid(0)
{
	StLock<Mutex> _(*this);
	
	// set parent session
	parent(Session::find(audit.sessionId(), true));

    // let's take a look at our wannabe client...
	if (mTaskPort.pid() != mPid) {
		secnotice("SS", "Task/pid setup mismatch pid=%d task=%d(%d)",
			mPid, mTaskPort.port(), mTaskPort.pid());
		CssmError::throwMe(CSSMERR_CSSM_ADDIN_AUTHENTICATE_FAILED);	// you lied!
	}

	setup(info);
	ClientIdentification::setup(this->pid());

    // NB: ServerChild::find() should only be used to determine
    // *existence*.  Don't use the returned Child object for anything else, 
    // as it is not protected against its underlying process's destruction.  
	if (this->pid() == getpid() // called ourselves (through some API). Do NOT record this as a "dirty" transaction
        || ServerChild::find<ServerChild>(this->pid()))   // securityd's child; do not mark this txn dirty
		VProc::Transaction::deactivate();

    secinfo("SS", "%p client new: pid:%d session:%d %s taskPort:%d uid:%d gid:%d", this, this->pid(), this->session().sessionId(),
             (char *)codePath(this->processCode()).c_str(), taskPort.port(), mUid, mGid);
}
//
// Notification message objects
//
Listener::Notification::Notification(NotificationDomain inDomain,
	NotificationEvent inEvent, uint32 seq, const CssmData &inData)
	: domain(inDomain), event(inEvent), sequence(seq), data(Allocator::standard(), inData)
{
	secinfo("notify", "%p notification created domain 0x%x event %d seq %d",
		this, domain, event, sequence);
}
示例#14
0
//
// Initialize the blob header for a given version
//
void CommonBlob::initialize(uint32 version)
{
    magic = magicNumber;

    secinfo("integrity", "creating a keychain with version %d", version);
    this->blobVersion = version;
}
示例#15
0
//
// What our (non-primary) load threads do
//
void MachServer::LoadThread::action()
{
	//@@@ race condition?! can server exit before helpers thread gets here?
	
	// register the worker thread and go
	server.addThread(this);
	try {
        secinfo("machserver", "start thread");
		server.runServerThread(true);
        secinfo("machserver", "end thread");
	} catch (...) {
		// fell out of server loop by error. Let the thread go quietly
        secinfo("machserver", "end thread (due to error)");
	}
	server.removeThread(this);
}
示例#16
0
//
// Clean up a Process object
//
Process::~Process()
{
    secinfo("SS", "%p client release: %d", this, this->pid());

    // release our name for the process's task port
	if (mTaskPort)
        mTaskPort.destroy();
}
示例#17
0
//
// Register a memory block for deferred release.
//
void MachServer::releaseWhenDone(Allocator &alloc, void *memory)
{
    if (memory) {
        set<Allocation> &releaseSet = perThread().deferredAllocations;
        assert(releaseSet.find(Allocation(memory, alloc)) == releaseSet.end());
        secinfo("machserver", "allocing register %p with alloc %p", memory, &alloc);
        releaseSet.insert(Allocation(memory, alloc));
    }
}
//
// Handle a port death or deallocation by removing all Listeners using that port.
// Returns true iff we had one.
//
bool Listener::remove(Port port)
{
    typedef ListenerMap::iterator Iterator;
    StLock<Mutex> _(setLock);
    pair<Iterator, Iterator> range = listeners.equal_range(port);
    if (range.first == range.second)
        return false;	// not one of ours

	assert(range.first != listeners.end());
	secinfo("notify", "remove port %d", port.port());
#if !defined(NDEBUG)
    for (Iterator it = range.first; it != range.second; it++) {
		assert(it->first == port);
		secinfo("notify", "%p listener removed", it->second.get());
	}
#endif //NDEBUG
    listeners.erase(range.first, range.second);
	port.destroy();
    return true;	// got it
}
//
// Listener basics
//
Listener::Listener(NotificationDomain dom, NotificationMask evs, mach_port_t port)
	: domain(dom), events(evs)
{
	assert(events);		// what's the point?
	
    // register in listener set
    StLock<Mutex> _(setLock);
    listeners.insert(ListenerMap::value_type(port, this));
	
	secinfo("notify", "%p created for domain 0x%x events 0x%x port %d",
		this, dom, evs, port);
}
RefPointer<Listener::Notification> Listener::JitterBuffer::popNotification()
{
	JBuffer::iterator it = mBuffer.find(mNotifyLast + 1);	// have next message?
	if (it == mBuffer.end())
		return NULL;			// nothing here
	else {
		RefPointer<Notification> result = it->second;	// save value
		mBuffer.erase(it);		// remove from buffer
		secinfo("notify-jit", "%p retrieved from jitter buffer", result.get());
		return result;			// return it
	}
}
示例#21
0
//
// Initiate service.
// This call will take control of the current thread and use it to service
// incoming requests. The thread will not be released until an error happens, which
// will cause an exception to be thrown. In other words, this never returns normally.
// We may also be creating additional threads to service concurrent requests
// as appropriate.
// @@@ Msg-errors in additional threads are not acted upon.
//
void MachServer::run(mach_msg_size_t maxSize, mach_msg_options_t options)
{
	// establish server-global (thread-shared) parameters
	mMaxSize = maxSize;
	mMsgOptions = options;
	
	// establish the thread pool state
	// (don't need managerLock since we're the only thread as of yet)
	idleCount = workerCount = 1;
	nextCheckTime = Time::now() + workerTimeout;
	leastIdleWorkers = 1;
	highestWorkerCount = 1;
	
	// run server loop in initial (immortal) thread
    secinfo("machserver", "start thread");
	runServerThread(false);
    secinfo("machserver", "end thread");
	
	// primary server thread exited somehow (not currently possible)
	assert(false);
}
//
// Jitter buffering
//
bool Listener::JitterBuffer::inSequence(Notification *message)
{
	if (message->sequence == mNotifyLast + 1) {	// next in sequence
		mNotifyLast++;			// record next sequence
		return true;			// go ahead
	} else {
		secinfo("notify-jit", "%p out of sequence (last %d got %d); buffering",
			message, mNotifyLast, message->sequence);
		mBuffer[message->sequence] = message;	// save for later
		return false;			// hold your fire
	}
}
示例#23
0
//
// Run through the accumulated deferred allocations and release them.
// This is done automatically on every pass through the server loop;
// it must be called by subclasses that implement their loop in some
// other way.
// @@@X Needs to be thread local
//
void MachServer::releaseDeferredAllocations()
{
    set<Allocation> &releaseSet = perThread().deferredAllocations;
	for (set<Allocation>::iterator it = releaseSet.begin(); it != releaseSet.end(); it++) {
        secinfo("machserver", "releasing alloc at %p with %p", it->addr, it->allocator);

        // before we release the deferred allocation, zap it so that secrets aren't left in memory
        size_t memSize = malloc_size(it->addr);
        bzero(it->addr, memSize);
		it->allocator->free(it->addr);
    }
	releaseSet.erase(releaseSet.begin(), releaseSet.end());
}
示例#24
0
bool MachServer::processTimer()
{
	Timer *top;
	{	StLock<Mutex> _(managerLock);	// could have multiple threads trying this
		if (!(top = static_cast<Timer *>(timers.pop(Time::now()))))
			return false;				// nothing (more) to be done now
	}	// drop lock; work has been retrieved
	try {
        secinfo("machserver", "timer start: %p, %d, %f", top, top->longTerm(), Time::now().internalForm());
		StLock<MachServer::Timer,
			&MachServer::Timer::select, &MachServer::Timer::unselect> _t(*top);
		if (top->longTerm()) {
			StLock<MachServer, &MachServer::busy, &MachServer::idle> _(*this);
			top->action();
		} else {
			top->action();
		}
        secinfo("machserver", "timer end (false)");
	} catch (...) {
        secinfo("machserver", "timer end (true)");
	}
	return true;
}
CFDataRef SecFDERecoveryUnwrapCRSKWithPrivKey(SecKeychainRef keychain, const FVPrivateKeyHeader *inHeader)
{
	CFDataRef result = NULL;
	OSStatus __secapiresult = 0;

	try
	{
		result = decodePrivateKeyHeader(keychain, Required(inHeader));
	}
	catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
	catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
	catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
	catch (...) { __secapiresult=errSecInternalComponent; }
	secinfo("FDERecovery", "SecFDERecoveryUnwrapCRSKWithPrivKey: %d", (int)__secapiresult);
	return result;
}
static void encodePrivateKeyHeader(const CssmData &inBlob, CFDataRef certificate, FVPrivateKeyHeader &outHeader)
{
	CssmClient::CL cl(gGuidAppleX509CL);
	const CssmData cert(const_cast<UInt8 *>(CFDataGetBytePtr(certificate)), CFDataGetLength(certificate));
	CSSM_KEY_PTR key;
	if (CSSM_RETURN rv = CSSM_CL_CertGetKeyInfo(cl->handle(), &cert, &key))
		CssmError::throwMe(rv);
	
	Security::CssmClient::CSP fCSP(gGuidAppleCSP);

	// Set it up so the cl is used to free key and key->KeyData
	// CssmAutoData _keyData(cl.allocator());
	// _keyData.set(CssmData::overlay(key->KeyData));
	CssmAutoData _key(cl.allocator());
	_key.set(reinterpret_cast<uint8 *>(key), sizeof(*key));
	CssmClient::Key cKey(fCSP, *key);
	
	/* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the 
		* associated key blob. 
		* Key is specified in CSSM_CSP_CreatePassThroughContext.
		* Hash is allocated by the CSP, in the App's memory, and returned
		* in *outData. */
	CssmClient::PassThrough passThrough(fCSP);
	passThrough.key(key);
	void *outData;
	passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
	CssmData *cssmData = reinterpret_cast<CssmData *>(outData);
	
	assert(cssmData->Length <= sizeof(outHeader.publicKeyHash));
	outHeader.publicKeyHashSize = (uint32_t)cssmData->Length;
	memcpy(outHeader.publicKeyHash, cssmData->Data, cssmData->Length);
	fCSP.allocator().free(cssmData->Data);
	fCSP.allocator().free(cssmData);
	
	/* Now encrypt the blob with the public key. */
    CssmClient::Encrypt encrypt(fCSP, key->KeyHeader.AlgorithmId);
	encrypt.key(cKey);
	CssmData clearBuf(outHeader.encryptedBlob, sizeof(outHeader.encryptedBlob));
	CssmAutoData remData(fCSP.allocator());
	encrypt.padding(CSSM_PADDING_PKCS1);
	
	outHeader.encryptedBlobSize = (uint32_t)encrypt.encrypt(inBlob, clearBuf, remData.get());
	if (outHeader.encryptedBlobSize > sizeof(outHeader.encryptedBlob))
		secinfo("FDERecovery", "encodePrivateKeyHeader: encrypted blob too big: %d", outHeader.encryptedBlobSize);
}
示例#27
0
//
// Construct the process-global state object.
// The ModuleNexus construction magic will ensure that this happens uniquely
// even if the face of multithreaded attack.
//
ClientSession::Global::Global()
{
    // find server port
	serverPort = findSecurityd();
    
	mach_port_t originPort = MACH_PORT_NULL;
	IPCN(ucsp_client_verifyPrivileged2(serverPort.port(), mig_get_reply_port(), &securitydCreds, &rcode, &originPort));
	if (originPort != serverPort.port())
		CssmError::throwMe(CSSM_ERRCODE_VERIFICATION_FAILURE);
	mach_port_mod_refs(mach_task_self(), originPort, MACH_PORT_RIGHT_SEND, -1);
	
    // send identification/setup message
	static const char extForm[] = "?:obsolete";
	ClientSetupInfo info = { 0x1234, SSPROTOVERSION };
	
    // cannot use UCSP_ARGS here because it uses mGlobal() -> deadlock
    Thread &thread = this->thread();
	
	IPCN(ucsp_client_setup(serverPort, thread.replyPort, &securitydCreds, &rcode,
		mach_task_self(), info, extForm));
    thread.registered = true;	// as a side-effect of setup call above
	IFDEBUG(serverPort.requestNotify(thread.replyPort));
	secinfo("SSclnt", "contact with %s established", mContactName);
}
CFDataRef decodePrivateKeyHeader(SecKeychainRef keychain, const FVPrivateKeyHeader &inHeader)
{	
	// kSecKeyLabel is defined in libsecurity_keychain/lib/SecKey.h
	SecKeychainAttribute attrs[] =
	{
		{ 6 /* kSecKeyLabel */, inHeader.publicKeyHashSize, const_cast<uint8 *>(inHeader.publicKeyHash) }
	};
	SecKeychainAttributeList attrList =
	{
		sizeof(attrs) / sizeof(SecKeychainAttribute),
		attrs
	};
	CSSM_CSP_HANDLE cspHandle = 0;
	const CSSM_KEY *cssmKey = NULL;
    const CSSM_ACCESS_CREDENTIALS *accessCred = NULL;
    CSSM_CC_HANDLE cc = 0;
	
	SecKeychainSearchRef _searchRef;
	throwIfError(SecKeychainSearchCreateFromAttributes(keychain, (SecItemClass) CSSM_DL_DB_RECORD_PRIVATE_KEY, &attrList, &_searchRef));
	CFRef<SecKeychainSearchRef> searchRef(_searchRef);
	
	SecKeychainItemRef _item;
    if (SecKeychainSearchCopyNext(searchRef, &_item) != 0) {
		return NULL;  // XXX possibly should throw here?
    }
	
	CFRef<SecKeyRef> keyItem(reinterpret_cast<SecKeyRef>(_item));
	throwIfError(SecKeyGetCSPHandle(keyItem, &cspHandle));
	throwIfError(SecKeyGetCSSMKey(keyItem, &cssmKey));
    throwIfError(SecKeyGetCredentials(keyItem, CSSM_ACL_AUTHORIZATION_DECRYPT, kSecCredentialTypeDefault, &accessCred));
    throwIfError(CSSM_CSP_CreateAsymmetricContext(cspHandle, cssmKey->KeyHeader.AlgorithmId, accessCred, cssmKey, CSSM_PADDING_PKCS1, &cc));
	CFDataRef result;
	
	try
	{		
		CssmMemoryFunctions memFuncs;
		throwIfError(CSSM_GetAPIMemoryFunctions(cspHandle, &memFuncs));
		CssmMemoryFunctionsAllocator allocator(memFuncs);
		
		const CssmData cipherBuf(const_cast<uint8 *>(inHeader.encryptedBlob), inHeader.encryptedBlobSize);
		CssmAutoData clearBuf(allocator);
		CssmAutoData remData(allocator);
		size_t bytesDecrypted;
		CSSM_RETURN crx = CSSM_DecryptData(cc, &cipherBuf, 1, &clearBuf.get(), 1, &bytesDecrypted, &remData.get());
		secinfo("FDERecovery", "decodePrivateKeyHeader: CSSM_DecryptData result: %d", crx);
		throwIfError(crx);
//		throwIfError(CSSM_DecryptData(cc, &cipherBuf, 1, &clearBuf.get(), 1, &bytesDecrypted, &remData.get()));
		clearBuf.length(bytesDecrypted);
//		rawKey.copy(clearBuf.get());
		result = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)clearBuf.get().data(), clearBuf.get().length());
//		result = parseKeyBlob(clearBuf.get());
	}
	catch(...)
	{
		CSSM_DeleteContext(cc);
		throw;
	}
	
	throwIfError(CSSM_DeleteContext(cc));
	
	return result;
}
示例#29
0
void MachServer::remove(Port receiver)
{
    secinfo("machserver", "port remove: %d", receiver.port());
	mPortSet -= receiver;
}
示例#30
0
//
// Add and remove extra listening ports.
// Messages directed to those ports are dispatched through the main handler.
// To get automatic call-out to another handler, use the Handler class.
//
void MachServer::add(Port receiver)
{
    secinfo("machserver", "port add: %d", receiver.port());
	mPortSet += receiver;
}