int BusyWithClassicConnection(EvalContext *ctx, ServerConnectionState *conn) { time_t tloc, trem = 0; char recvbuffer[CF_BUFSIZE + CF_BUFEXT], check[CF_BUFSIZE]; char sendbuffer[CF_BUFSIZE] = { 0 }; char filename[CF_BUFSIZE], buffer[CF_BUFSIZE], args[CF_BUFSIZE], out[CF_BUFSIZE]; long time_no_see = 0; unsigned int len = 0; int drift, plainlen, received, encrypted = 0; ServerFileGetState get_args; Item *classes; memset(recvbuffer, 0, CF_BUFSIZE + CF_BUFEXT); memset(&get_args, 0, sizeof(get_args)); received = ReceiveTransaction(conn->conn_info, recvbuffer, NULL); if (received == -1 || received == 0) { return false; } if (strlen(recvbuffer) == 0) { Log(LOG_LEVEL_WARNING, "Got NULL transmission, skipping!"); return true; } /* Don't process request if we're signalled to exit. */ if (IsPendingTermination()) { return false; } ProtocolCommandClassic command = GetCommandClassic(recvbuffer); switch (command) { /* Plain text authentication; this MUST be the first command client using classic protocol is sending. */ case PROTOCOL_COMMAND_AUTH_PLAIN: SetConnectionData(conn, (char *) (recvbuffer + strlen("CAUTH "))); if (conn->username == NULL || IsUserNameValid(conn->username) == false) { Log(LOG_LEVEL_INFO, "Client is sending wrong username: '******'", conn->username); RefuseAccess(conn, recvbuffer); return false; } /* This is used only for forcing correct state of state machine while connecting and authenticating user using classic protocol. */ conn->user_data_set = true; return true; /* This MUST be exactly second command client using classic protocol is sending. This is where key agreement takes place. */ case PROTOCOL_COMMAND_AUTH_SECURE: /* First command was ommited by client; this is protocol violation. */ if (!conn->user_data_set) { Log(LOG_LEVEL_INFO, "Client is not verified; rejecting connection"); RefuseAccess(conn, recvbuffer); return false; } conn->rsa_auth = AuthenticationDialogue(conn, recvbuffer, received); if (!conn->rsa_auth) { Log(LOG_LEVEL_INFO, "Auth dialogue error"); RefuseAccess(conn, recvbuffer); return false; } return true; default: break; } /* At this point we should have both user_data_set and rsa_auth set to perform any operation. We can check only for second one as without first it won't be set up. */ if (!conn->rsa_auth) { Log(LOG_LEVEL_INFO, "Server refusal due to no RSA authentication [command: %d]", command); RefuseAccess(conn, recvbuffer); return false; } /* We have to have key at this point. */ assert(conn->session_key); /* At this point we can safely do next switch and make sure user is authenticated. */ switch (command) { case PROTOCOL_COMMAND_EXEC: memset(args, 0, CF_BUFSIZE); sscanf(recvbuffer, "EXEC %255[^\n]", args); if (!AllowedUser(conn->username)) { Log(LOG_LEVEL_INFO, "Server refusal due to non-allowed user"); RefuseAccess(conn, recvbuffer); return false; } if (!AccessControl(ctx, CommandArg0(CFRUNCOMMAND), conn, false)) { Log(LOG_LEVEL_INFO, "Server refusal due to denied access to requested object"); RefuseAccess(conn, recvbuffer); return false; } if (!MatchClasses(ctx, conn)) { Log(LOG_LEVEL_INFO, "Server refusal due to failed class/context match"); Terminate(conn->conn_info); return false; } DoExec(ctx, conn, args); Terminate(conn->conn_info); return false; case PROTOCOL_COMMAND_VERSION: snprintf(sendbuffer, sizeof(sendbuffer), "OK: %s", Version()); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); return conn->user_data_set; case PROTOCOL_COMMAND_GET: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "GET %d %[^\n]", &(get_args.buf_size), filename); if ((get_args.buf_size < 0) || (get_args.buf_size > CF_BUFSIZE)) { Log(LOG_LEVEL_INFO, "GET buffer out of bounds"); RefuseAccess(conn, recvbuffer); return false; } if (!AccessControl(ctx, filename, conn, false)) { Log(LOG_LEVEL_INFO, "Access denied to get object"); RefuseAccess(conn, recvbuffer); return true; } memset(sendbuffer, 0, sizeof(sendbuffer)); if (get_args.buf_size >= CF_BUFSIZE) { get_args.buf_size = 2048; } get_args.connect = conn; get_args.encrypt = false; get_args.replybuff = sendbuffer; get_args.replyfile = filename; CfGetFile(&get_args); return true; case PROTOCOL_COMMAND_GET_SECURE: memset(buffer, 0, CF_BUFSIZE); sscanf(recvbuffer, "SGET %u %d", &len, &(get_args.buf_size)); if (received != len + CF_PROTO_OFFSET) { Log(LOG_LEVEL_VERBOSE, "Protocol error SGET"); RefuseAccess(conn, recvbuffer); return false; } plainlen = DecryptString(conn->encryption_type, recvbuffer + CF_PROTO_OFFSET, buffer, conn->session_key, len); cfscanf(buffer, strlen("GET"), strlen("dummykey"), check, sendbuffer, filename); if (strcmp(check, "GET") != 0) { Log(LOG_LEVEL_INFO, "SGET/GET problem"); RefuseAccess(conn, recvbuffer); return true; } if ((get_args.buf_size < 0) || (get_args.buf_size > 8192)) { Log(LOG_LEVEL_INFO, "SGET bounding error"); RefuseAccess(conn, recvbuffer); return false; } if (get_args.buf_size >= CF_BUFSIZE) { get_args.buf_size = 2048; } Log(LOG_LEVEL_DEBUG, "Confirm decryption, and thus validity of caller"); Log(LOG_LEVEL_DEBUG, "SGET '%s' with blocksize %d", filename, get_args.buf_size); if (!AccessControl(ctx, filename, conn, true)) { Log(LOG_LEVEL_INFO, "Access control error"); RefuseAccess(conn, recvbuffer); return false; } memset(sendbuffer, 0, sizeof(sendbuffer)); get_args.connect = conn; get_args.encrypt = true; get_args.replybuff = sendbuffer; get_args.replyfile = filename; CfEncryptGetFile(&get_args); return true; case PROTOCOL_COMMAND_OPENDIR_SECURE: memset(buffer, 0, CF_BUFSIZE); sscanf(recvbuffer, "SOPENDIR %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_VERBOSE, "Protocol error OPENDIR: %d", len); RefuseAccess(conn, recvbuffer); return false; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "OPENDIR", 7) != 0) { Log(LOG_LEVEL_INFO, "Opendir failed to decrypt"); RefuseAccess(conn, recvbuffer); return true; } memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "OPENDIR %[^\n]", filename); if (!AccessControl(ctx, filename, conn, true)) /* opendir don't care about privacy */ { Log(LOG_LEVEL_INFO, "Access error"); RefuseAccess(conn, recvbuffer); return false; } CfSecOpenDirectory(conn, sendbuffer, filename); return true; case PROTOCOL_COMMAND_OPENDIR: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "OPENDIR %[^\n]", filename); if (!AccessControl(ctx, filename, conn, true)) /* opendir don't care about privacy */ { Log(LOG_LEVEL_INFO, "DIR access error"); RefuseAccess(conn, recvbuffer); return false; } CfOpenDirectory(conn, sendbuffer, filename); return true; case PROTOCOL_COMMAND_SYNC_SECURE: memset(buffer, 0, CF_BUFSIZE); sscanf(recvbuffer, "SSYNCH %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_VERBOSE, "Protocol error SSYNCH: %d", len); RefuseAccess(conn, recvbuffer); return false; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (plainlen < 0) { DebugBinOut((char *) conn->session_key, 32, "Session key"); Log(LOG_LEVEL_ERR, "Bad decrypt (%d)", len); } if (strncmp(recvbuffer, "SYNCH", 5) != 0) { Log(LOG_LEVEL_INFO, "No synch"); RefuseAccess(conn, recvbuffer); return true; } /* roll through, no break */ case PROTOCOL_COMMAND_SYNC: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "SYNCH %ld STAT %[^\n]", &time_no_see, filename); trem = (time_t) time_no_see; if ((time_no_see == 0) || (filename[0] == '\0')) { break; } if ((tloc = time((time_t *) NULL)) == -1) { Log(LOG_LEVEL_INFO, "Couldn't read system clock. (time: %s)", GetErrorStr()); SendTransaction(conn->conn_info, "BAD: clocks out of synch", 0, CF_DONE); return true; } drift = (int) (tloc - trem); if (!AccessControl(ctx, filename, conn, true)) { Log(LOG_LEVEL_INFO, "Access control in sync"); RefuseAccess(conn, recvbuffer); return true; } if (DENYBADCLOCKS && (drift * drift > CLOCK_DRIFT * CLOCK_DRIFT)) { snprintf(sendbuffer, sizeof(sendbuffer), "BAD: Clocks are too far unsynchronized %ld/%ld", (long) tloc, (long) trem); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); return true; } else { Log(LOG_LEVEL_DEBUG, "Clocks were off by %ld", (long) tloc - (long) trem); StatFile(conn, sendbuffer, filename); } return true; case PROTOCOL_COMMAND_MD5_SECURE: sscanf(recvbuffer, "SMD5 %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decryption error"); RefuseAccess(conn, recvbuffer); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "MD5", 3) != 0) { Log(LOG_LEVEL_INFO, "MD5 protocol error"); RefuseAccess(conn, recvbuffer); return false; } /* roll through, no break */ case PROTOCOL_COMMAND_MD5: CompareLocalHash(conn, sendbuffer, recvbuffer); return true; case PROTOCOL_COMMAND_VAR_SECURE: sscanf(recvbuffer, "SVAR %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error SVAR"); RefuseAccess(conn, "decrypt error SVAR"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); encrypted = true; if (strncmp(recvbuffer, "VAR", 3) != 0) { Log(LOG_LEVEL_INFO, "VAR protocol defect"); RefuseAccess(conn, "decryption failure"); return false; } /* roll through, no break */ case PROTOCOL_COMMAND_VAR: if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted)) { Log(LOG_LEVEL_INFO, "Literal access failure"); RefuseAccess(conn, recvbuffer); return false; } GetServerLiteral(ctx, conn, sendbuffer, recvbuffer, encrypted); return true; case PROTOCOL_COMMAND_CONTEXT_SECURE: sscanf(recvbuffer, "SCONTEXT %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error SCONTEXT, len,received = %d,%d", len, received); RefuseAccess(conn, "decrypt error SCONTEXT"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); encrypted = true; if (strncmp(recvbuffer, "CONTEXT", 7) != 0) { Log(LOG_LEVEL_INFO, "CONTEXT protocol defect..."); RefuseAccess(conn, "Decryption failed?"); return false; } /* roll through, no break */ case PROTOCOL_COMMAND_CONTEXT: if ((classes = ContextAccessControl(ctx, recvbuffer, conn, encrypted)) == NULL) { Log(LOG_LEVEL_INFO, "Context access failure on %s", recvbuffer); RefuseAccess(conn, recvbuffer); return false; } ReplyServerContext(conn, encrypted, classes); return true; case PROTOCOL_COMMAND_QUERY_SECURE: sscanf(recvbuffer, "SQUERY %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error SQUERY"); RefuseAccess(conn, "decrypt error SQUERY"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "QUERY", 5) != 0) { Log(LOG_LEVEL_INFO, "QUERY protocol defect"); RefuseAccess(conn, "decryption failure"); return false; } if (!LiteralAccessControl(ctx, recvbuffer, conn, true)) { Log(LOG_LEVEL_INFO, "Query access failure"); RefuseAccess(conn, recvbuffer); return false; } if (GetServerQuery(conn, recvbuffer, true)) /* always encrypt */ { return true; } break; case PROTOCOL_COMMAND_CALL_ME_BACK: sscanf(recvbuffer, "SCALLBACK %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error CALL_ME_BACK"); RefuseAccess(conn, "decrypt error CALL_ME_BACK"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "CALL_ME_BACK collect_calls", strlen("CALL_ME_BACK collect_calls")) != 0) { Log(LOG_LEVEL_INFO, "CALL_ME_BACK protocol defect"); RefuseAccess(conn, "decryption failure"); return false; } if (!LiteralAccessControl(ctx, recvbuffer, conn, true)) { Log(LOG_LEVEL_INFO, "Query access failure"); RefuseAccess(conn, recvbuffer); return false; } if (ReceiveCollectCall(conn)) { return true; } case PROTOCOL_COMMAND_AUTH_PLAIN: case PROTOCOL_COMMAND_AUTH_SECURE: case PROTOCOL_COMMAND_AUTH: case PROTOCOL_COMMAND_CONTEXTS: case PROTOCOL_COMMAND_BAD: Log(LOG_LEVEL_WARNING, "Unexpected protocol command"); } strcpy(sendbuffer, "BAD: Request denied"); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); Log(LOG_LEVEL_INFO, "Closing connection, due to request: '%s'", recvbuffer); return false; }
bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) { time_t tloc, trem = 0; char recvbuffer[CF_BUFSIZE + CF_BUFEXT], sendbuffer[CF_BUFSIZE]; char filename[CF_BUFSIZE], args[CF_BUFSIZE], out[CF_BUFSIZE]; long time_no_see = 0; unsigned int len = 0; int drift, received; ServerFileGetState get_args; Item *classes; /* We never double encrypt within the TLS layer */ const int encrypted = 0; memset(recvbuffer, 0, CF_BUFSIZE + CF_BUFEXT); memset(&get_args, 0, sizeof(get_args)); received = ReceiveTransaction(&conn->conn_info, recvbuffer, NULL); if (received == -1 || received == 0) { return false; } if (strlen(recvbuffer) == 0) { Log(LOG_LEVEL_WARNING, "Got NULL transmission, skipping!"); return true; } /* Don't process request if we're signalled to exit. */ if (IsPendingTermination()) { return false; } switch (GetCommandNew(recvbuffer)) { case PROTOCOL_COMMAND_EXEC: memset(args, 0, CF_BUFSIZE); sscanf(recvbuffer, "EXEC %255[^\n]", args); if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "Server refusal due to incorrect identity"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!AllowedUser(conn->username)) { Log(LOG_LEVEL_INFO, "Server refusal due to non-allowed user"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!conn->rsa_auth) { Log(LOG_LEVEL_INFO, "Server refusal due to no RSA authentication"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!AccessControl(ctx, CommandArg0(CFRUNCOMMAND), conn, false)) { Log(LOG_LEVEL_INFO, "Server refusal due to denied access to requested object"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!MatchClasses(ctx, conn)) { Log(LOG_LEVEL_INFO, "Server refusal due to failed class/context match"); Terminate(&conn->conn_info); return false; } DoExec(ctx, conn, args); Terminate(&conn->conn_info); return false; case PROTOCOL_COMMAND_VERSION: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); } snprintf(conn->output, CF_BUFSIZE, "OK: %s", Version()); SendTransaction(&conn->conn_info, conn->output, 0, CF_DONE); return conn->id_verified; case PROTOCOL_COMMAND_GET: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "GET %d %[^\n]", &(get_args.buf_size), filename); if ((get_args.buf_size < 0) || (get_args.buf_size > CF_BUFSIZE)) { Log(LOG_LEVEL_INFO, "GET buffer out of bounds"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!AccessControl(ctx, filename, conn, false)) { Log(LOG_LEVEL_INFO, "Access denied to get object"); RefuseAccess(conn, 0, recvbuffer); return true; } memset(sendbuffer, 0, CF_BUFSIZE); if (get_args.buf_size >= CF_BUFSIZE) { get_args.buf_size = 2048; } get_args.connect = conn; get_args.encrypt = false; get_args.replybuff = sendbuffer; get_args.replyfile = filename; CfGetFile(&get_args); return true; case PROTOCOL_COMMAND_OPENDIR: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "OPENDIR %[^\n]", filename); if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!AccessControl(ctx, filename, conn, true)) /* opendir don't care about privacy */ { Log(LOG_LEVEL_INFO, "DIR access error"); RefuseAccess(conn, 0, recvbuffer); return false; } CfOpenDirectory(conn, sendbuffer, filename); return true; case PROTOCOL_COMMAND_SYNC: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return false; } memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "SYNCH %ld STAT %[^\n]", &time_no_see, filename); trem = (time_t) time_no_see; if ((time_no_see == 0) || (filename[0] == '\0')) { break; } if ((tloc = time((time_t *) NULL)) == -1) { sprintf(conn->output, "Couldn't read system clock\n"); Log(LOG_LEVEL_INFO, "Couldn't read system clock. (time: %s)", GetErrorStr()); SendTransaction(&conn->conn_info, "BAD: clocks out of synch", 0, CF_DONE); return true; } drift = (int) (tloc - trem); if (!AccessControl(ctx, filename, conn, true)) { Log(LOG_LEVEL_VERBOSE, "AccessControl: access denied"); RefuseAccess(conn, 0, recvbuffer); return true; } if (DENYBADCLOCKS && (drift * drift > CLOCK_DRIFT * CLOCK_DRIFT)) { snprintf(conn->output, CF_BUFSIZE - 1, "BAD: Clocks are too far unsynchronized %ld/%ld\n", (long) tloc, (long) trem); SendTransaction(&conn->conn_info, conn->output, 0, CF_DONE); return true; } else { Log(LOG_LEVEL_DEBUG, "Clocks were off by %ld", (long) tloc - (long) trem); StatFile(conn, sendbuffer, filename); } return true; case PROTOCOL_COMMAND_MD5: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return true; } CompareLocalHash(conn, sendbuffer, recvbuffer); return true; case PROTOCOL_COMMAND_VAR: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return true; } if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted)) { Log(LOG_LEVEL_INFO, "Literal access failure"); RefuseAccess(conn, 0, recvbuffer); return false; } GetServerLiteral(ctx, conn, sendbuffer, recvbuffer, encrypted); return true; case PROTOCOL_COMMAND_CONTEXT: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, "Context probe"); return true; } if ((classes = ContextAccessControl(ctx, recvbuffer, conn, encrypted)) == NULL) { Log(LOG_LEVEL_INFO, "Context access failure on %s", recvbuffer); RefuseAccess(conn, 0, recvbuffer); return false; } ReplyServerContext(conn, encrypted, classes); return true; case PROTOCOL_COMMAND_QUERY: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return true; } if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted)) { Log(LOG_LEVEL_INFO, "Query access failure"); RefuseAccess(conn, 0, recvbuffer); return false; } if (GetServerQuery(conn, recvbuffer, encrypted)) { return true; } break; case PROTOCOL_COMMAND_CALL_ME_BACK: sscanf(recvbuffer, "SCALLBACK %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error CALL_ME_BACK"); RefuseAccess(conn, 0, "decrypt error CALL_ME_BACK"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "CALL_ME_BACK collect_calls", strlen("CALL_ME_BACK collect_calls")) != 0) { Log(LOG_LEVEL_INFO, "CALL_ME_BACK protocol defect"); RefuseAccess(conn, 0, "decryption failure"); return false; } if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return true; } if (!LiteralAccessControl(ctx, recvbuffer, conn, true)) { Log(LOG_LEVEL_INFO, "Query access failure"); RefuseAccess(conn, 0, recvbuffer); return false; } return ReceiveCollectCall(conn); case PROTOCOL_COMMAND_BAD: Log(LOG_LEVEL_WARNING, "Unexpected protocol command: %s", recvbuffer); } sprintf(sendbuffer, "BAD: Request denied\n"); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); Log(LOG_LEVEL_INFO, "Closing connection, due to request: '%s'", recvbuffer); return false; }