NTSTATUS tdi_receive_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(Irp); struct ot_entry *ote_conn; KIRQL irql; KdPrint(("[tdi_fw] tdi_receive_complete: connobj: 0x%x; status: 0x%x; received: %u\n", irps->FileObject, Irp->IoStatus.Status, Irp->IoStatus.Information)); ote_conn = ot_find_fileobj(irps->FileObject, &irql); if (ote_conn != NULL) { ULONG bytes = Irp->IoStatus.Information; ote_conn->bytes_in += bytes; // traffic stats KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); g_traffic[TRAFFIC_TOTAL_IN] += bytes; if (ote_conn->log_disconnect) g_traffic[TRAFFIC_COUNTED_IN] += bytes; KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); KeReleaseSpinLock(&g_ot_hash_guard, irql); } return tdi_generic_complete(DeviceObject, Irp, Context); }
/* * TDI_DISASSOCIATE_ADDRESS handler */ int tdi_disassociate_address(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) { struct ot_entry *ote_conn = NULL; KIRQL irql; NTSTATUS status; KdPrint(("[tdi_fw] tdi_disassociate_address: connobj 0x%x\n", irps->FileObject)); // delete connnection object ote_conn = ot_find_fileobj(irps->FileObject, &irql); if (ote_conn == NULL) { KdPrint(("[tdi_fw] tdi_disassociate_address: ot_find_fileobj(0x%x)\n", irps->FileObject)); goto done; } // delete link of (addrobj, conn_ctx)->connobj status = ot_del_conn_ctx(ote_conn->associated_fileobj, ote_conn->conn_ctx); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_disassociate_address: ot_del_conn_ctx: 0x%x\n", status)); goto done; } done: if (ote_conn != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); // success anyway return FILTER_ALLOW; }
int tdi_send(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) { TDI_REQUEST_KERNEL_SEND *param = (TDI_REQUEST_KERNEL_SEND *)(&irps->Parameters); struct ot_entry *ote_conn; KIRQL irql; KdPrint(("[tdi_fw] tdi_send: connobj: 0x%x; SendLength: %u; SendFlags: 0x%x\n", irps->FileObject, param->SendLength, param->SendFlags)); ote_conn = ot_find_fileobj(irps->FileObject, &irql); if (ote_conn != NULL) { ULONG bytes = param->SendLength; ote_conn->bytes_out += bytes; // traffic stats KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); g_traffic[TRAFFIC_TOTAL_OUT] += bytes; if (ote_conn->log_disconnect) g_traffic[TRAFFIC_COUNTED_OUT] += bytes; KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); KeReleaseSpinLock(&g_ot_hash_guard, irql); } // TODO: process TDI_SEND_AND_DISCONNECT flag (used by IIS for example) return FILTER_ALLOW; }
/* this completion routine gets address and port from reply to TDI_QUERY_ADDRESS_INFO */ NTSTATUS tdi_create_addrobj_complete2(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { NTSTATUS status; TDI_CREATE_ADDROBJ2_CTX *ctx = (TDI_CREATE_ADDROBJ2_CTX *)Context; TA_ADDRESS *addr = ctx->tai->Address.Address; struct ot_entry *ote_addr; KIRQL irql; int ipproto; KdPrint(("[tdi_fw] tdi_create_addrobj_complete2: address: %x:%u\n", ntohl(((TDI_ADDRESS_IP *)(addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(addr->Address))->sin_port))); // save address ote_addr = ot_find_fileobj(ctx->fileobj, &irql); if (ote_addr == NULL) { KdPrint(("[tdi_fw] tdi_create_addrobj_complete2: ot_find_fileobj(0x%x)\n", ctx->fileobj)); status = STATUS_OBJECT_NAME_NOT_FOUND; goto done; } if (addr->AddressLength > sizeof(ote_addr->local_addr)) { KdPrint(("[tdi_fw] tdi_create_addrobj_complete2: address too long! (%u)\n", addr->AddressLength)); status = STATUS_BUFFER_OVERFLOW; goto done; } memcpy(ote_addr->local_addr, addr, addr->AddressLength); if (ote_addr->ipproto != IPPROTO_TCP) { // set "LISTEN" state for this addrobj status = add_listen(ote_addr); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_create_addrobj_complete2: add_listen: 0x%x!\n", status)); goto done; } } status = STATUS_SUCCESS; done: if (ote_addr != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); // cleanup MDL to avoid unlocking pages from NonPaged pool if (Irp->MdlAddress != NULL) { IoFreeMdl(Irp->MdlAddress); Irp->MdlAddress = NULL; } free(ctx->tai); free(ctx); // success anyway return STATUS_SUCCESS; }
/* * TDI_ASSOCIATE_ADDRESS handler * * With help of this routine we can get address object by connection object * and get connection object by connection context and address object */ int tdi_associate_address(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) { HANDLE addr_handle = ((TDI_REQUEST_KERNEL_ASSOCIATE *)(&irps->Parameters))->AddressHandle; PFILE_OBJECT addrobj = NULL; NTSTATUS status; struct ot_entry *ote_conn = NULL; KIRQL irql; int result = FILTER_DENY; KdPrint(("[tdi_fw] tdi_associate_address: devobj 0x%x; connobj 0x%x\n", irps->DeviceObject, irps->FileObject)); status = ObReferenceObjectByHandle(addr_handle, GENERIC_READ, NULL, KernelMode, &addrobj, NULL); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_associate_address: ObReferenceObjectByHandle: 0x%x\n", status)); goto done; } KdPrint(("[tdi_fw] tdi_associate_address: connobj = 0x%x ---> addrobj = 0x%x\n", irps->FileObject, addrobj)); // associate addrobj with connobj ote_conn = ot_find_fileobj(irps->FileObject, &irql); if (ote_conn == NULL) { KdPrint(("[tdi_fw] tdi_associate_address: ot_find_fileobj(0x%x)\n", irps->FileObject)); goto done; } ote_conn->associated_fileobj = addrobj; // add (conn_ctx, addrobj)->connobj status = ot_add_conn_ctx(addrobj, ote_conn->conn_ctx, irps->FileObject); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_associate_address: ot_add_conn_ctx: 0x%x\n", status)); goto done; } result = FILTER_ALLOW; done: if (addrobj != NULL) ObDereferenceObject(addrobj); // cleanup if (ote_conn != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); return result; }
NTSTATUS tdi_event_chained_receive( IN PVOID TdiEventContext, IN CONNECTION_CONTEXT ConnectionContext, IN ULONG ReceiveFlags, IN ULONG ReceiveLength, IN ULONG StartingOffset, IN PMDL Tsdu, IN PVOID TsduDescriptor) { TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; PFILE_OBJECT connobj = ot_find_conn_ctx(ctx->fileobj, ConnectionContext); NTSTATUS status; KdPrint(("[tdi_fw] tdi_event_chained_receive: addrobj 0x%x; connobj: 0x%x; %u; flags: 0x%x\n", ctx->fileobj, connobj, ReceiveLength, ReceiveFlags)); status = ((PTDI_IND_CHAINED_RECEIVE)(ctx->old_handler)) (ctx->old_context, ConnectionContext, ReceiveFlags,ReceiveLength , StartingOffset, Tsdu, TsduDescriptor); KdPrint(("[tdi_fw] tdi_event_chained_receive: status 0x%x\n", status)); if (status == STATUS_SUCCESS || status == STATUS_PENDING) { struct ot_entry *ote_conn; KIRQL irql; ote_conn = ot_find_fileobj(connobj, &irql); if (ote_conn != NULL) { ULONG bytes = ReceiveLength; ote_conn->bytes_in += bytes; // traffic stats KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); g_traffic[TRAFFIC_TOTAL_IN] += bytes; if (ote_conn->log_disconnect) g_traffic[TRAFFIC_COUNTED_IN] += bytes; KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); KeReleaseSpinLock(&g_ot_hash_guard, irql); } } return status; }
NTSTATUS tdi_event_connect( IN PVOID TdiEventContext, IN LONG RemoteAddressLength, IN PVOID RemoteAddress, IN LONG UserDataLength, IN PVOID UserData, IN LONG OptionsLength, IN PVOID Options, OUT CONNECTION_CONTEXT *ConnectionContext, OUT PIRP *AcceptIrp) { TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; TA_ADDRESS *remote_addr = ((TRANSPORT_ADDRESS *)RemoteAddress)->Address, *local_addr; struct ot_entry *ote_addr = NULL, *ote_conn = NULL; KIRQL irql; struct flt_request request; struct flt_rule rule; int result = FILTER_DENY; NTSTATUS status; PIO_STACK_LOCATION irps = NULL; struct accept_param *param = NULL; memset(&request, 0, sizeof(request)); KdPrint(("[tdi_fw] tdi_event_connect: addrobj 0x%x\n", ctx->fileobj)); ote_addr = ot_find_fileobj(ctx->fileobj, &irql); if (ote_addr == NULL) { KdPrint(("[tdi_fw] tdi_event_connect: ot_find_fileobj(0x%x)\n", ctx->fileobj)); goto done; } local_addr = (TA_ADDRESS *)(ote_addr->local_addr); KdPrint(("[tdi_fw] tdi_event_connect(pid:%u): %x:%u -> %x:%u\n", ote_addr->pid, ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port), ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port))); /* * request quick filter */ request.struct_size = sizeof(request); request.type = TYPE_CONNECT; request.direction = DIRECTION_IN; request.proto = IPPROTO_TCP; request.pid = ote_addr->pid; // get user SID & attributes! if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) request.sid_a_size = ote_addr->sid_a_size; memcpy(&request.addr.from, &remote_addr->AddressType, sizeof(struct sockaddr)); memcpy(&request.addr.to, &local_addr->AddressType, sizeof(struct sockaddr)); request.addr.len = sizeof(struct sockaddr_in); result = quick_filter(&request, &rule); memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); // log request later if (result == FILTER_DENY) goto done; result = FILTER_DENY; // leave spinlock before calling original handler KeReleaseSpinLock(&g_ot_hash_guard, irql); ote_addr = NULL; /* * run original handler */ status = ((PTDI_IND_CONNECT)(ctx->old_handler)) (ctx->old_context, RemoteAddressLength, RemoteAddress, UserDataLength, UserData, OptionsLength, Options, ConnectionContext, AcceptIrp); if (status != STATUS_MORE_PROCESSING_REQUIRED || *AcceptIrp == NULL) { KdPrint(("[tdi_fw] tdi_event_connect: status from original handler: 0x%x\n", status)); goto done; } /* * reinitialize connobj */ irps = IoGetCurrentIrpStackLocation(*AcceptIrp); KdPrint(("[tdi_fw] tdi_event_connect: connobj 0x%x\n", irps->FileObject)); // patch *AcceptIrp to change completion routine param = (struct accept_param *)malloc_np(sizeof(*param)); if (param == NULL) { KdPrint(("[tdi_fw] tdi_event_connect: malloc_np!\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto done; } param->old_cr = irps->CompletionRoutine; param->old_context = irps->Context; param->fileobj = irps->FileObject; param->old_control = irps->Control; // can't use IoSetCompletionRoutine because it uses next not current stack location irps->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; irps->CompletionRoutine = tdi_evconn_accept_complete; irps->Context = param; param = NULL; // find connobj for changing ote_conn = ot_find_fileobj(irps->FileObject, &irql); if (ote_conn == NULL) { KdPrint(("[tdi_fw] tdi_event_connect: ot_find_fileobj(0x%x)\n", irps->FileObject)); status = STATUS_OBJECT_NAME_NOT_FOUND; goto done; } ASSERT(ote_conn->type == FILEOBJ_CONNOBJ); // connobj must be associated with addrobj! if (ote_conn->associated_fileobj != ctx->fileobj) { KdPrint(("[tdi_fw] tdi_event_connect: 0x%x != 0x%x\n", ote_conn->associated_fileobj, ctx->fileobj)); status = STATUS_INVALID_PARAMETER; goto done; } // change conn_ctx (if needed) if (ote_conn->conn_ctx != *ConnectionContext) { // update (conn_ctx, addrobj)->connobj status = ot_del_conn_ctx(ote_conn->associated_fileobj, ote_conn->conn_ctx); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_event_connect: ot_del_conn_ctx: 0x%x\n", status)); goto done; } ote_conn->conn_ctx = *ConnectionContext; status = ot_add_conn_ctx(ote_conn->associated_fileobj, ote_conn->conn_ctx, irps->FileObject); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_event_connect: ot_add_conn_ctx: 0x%x\n", status)); goto done; } } // clear listen & conn entries in connobj (fileobject can be reused) ASSERT(ote_conn->listen_entry == NULL); if (ote_conn->listen_entry != NULL) del_listen_obj(ote_conn->listen_entry, FALSE); // free build case if (ote_conn->conn_entry != NULL) { if (ote_conn->ipproto == IPPROTO_TCP && ote_conn->log_disconnect) log_disconnect(ote_conn); del_tcp_conn_obj(ote_conn->conn_entry, FALSE); } // clear bytes count ote_conn->bytes_in = ote_conn->bytes_out = 0; // setup log_disconnect flag from rule ote_conn->log_disconnect = (rule.log >= RULE_LOG_COUNT); // sanity check if (local_addr->AddressLength != remote_addr->AddressLength) { KdPrint(("[tdi_fw] tdi_event_connect: different addr lengths! (%u != %u)\n", local_addr->AddressLength, remote_addr->AddressLength)); status = STATUS_INFO_LENGTH_MISMATCH; goto done; } // associate remote address with connobj if (remote_addr->AddressLength > sizeof(ote_conn->remote_addr)) { KdPrint(("[tdi_fw] tdi_event_connect: address too long! (%u)\n", remote_addr->AddressLength)); status = STATUS_BUFFER_TOO_SMALL; goto done; } memcpy(ote_conn->remote_addr, remote_addr, remote_addr->AddressLength); // associate local address with connobj if (local_addr->AddressLength > sizeof(ote_conn->local_addr)) { KdPrint(("[tdi_fw] tdi_event_connect: address too long! (%u)\n", local_addr->AddressLength)); status = STATUS_BUFFER_TOO_SMALL; goto done; } memcpy(ote_conn->local_addr, local_addr, local_addr->AddressLength); // create connection with "SYN_RCVD" state status = add_tcp_conn(ote_conn, TCP_STATE_SYN_RCVD); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_event_connect: add_tcp_conn: 0x%x\n", status)); goto done; } result = FILTER_ALLOW; done: // if logging is needed log request if (rule.log >= RULE_LOG_LOG) { if (result != FILTER_ALLOW && rule.result == FILTER_ALLOW) { request.type = TYPE_CONNECT_ERROR; // error has been occured request.status = status; } log_request(&request); } if (result != FILTER_ALLOW) { // deny incoming connection KdPrint(("[tdi_fw] tdi_event_connect: deny on reason 0x%x\n", status)); if (irps != NULL) { // delete connection if (ote_conn != NULL && ote_conn->conn_entry != NULL) { del_tcp_conn_obj(ote_conn->conn_entry, FALSE); ote_conn->conn_entry = NULL; } // release spinlock before IoCompleteRequest to avoid completion call inside spinlock if (ote_addr != NULL || ote_conn != NULL) { KeReleaseSpinLock(&g_ot_hash_guard, irql); ote_addr = NULL; ote_conn = NULL; } // destroy accepted IRP (*AcceptIrp)->IoStatus.Status = STATUS_UNSUCCESSFUL; IoCompleteRequest(*AcceptIrp, IO_NO_INCREMENT); } *AcceptIrp = NULL; status = STATUS_CONNECTION_REFUSED; } else status = STATUS_MORE_PROCESSING_REQUIRED; // cleanup if (ote_addr != NULL || ote_conn != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); if (param != NULL) free(param); if (request.sid_a != NULL) free(request.sid_a); return status; }
int tdi_connect(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) { PTDI_REQUEST_KERNEL_CONNECT param = (PTDI_REQUEST_KERNEL_CONNECT)(&irps->Parameters); TA_ADDRESS *remote_addr = ((TRANSPORT_ADDRESS *)(param->RequestConnectionInformation->RemoteAddress))->Address; PFILE_OBJECT addrobj; NTSTATUS status; TA_ADDRESS *local_addr; int result = FILTER_DENY, ipproto; struct ot_entry *ote_conn = NULL, *ote_addr; KIRQL irql; struct flt_request request; struct flt_rule rule; memset(&request, 0, sizeof(request)); KdPrint(("[tdi_fw] tdi_connect: connobj 0x%x, to address %x:%u\n", irps->FileObject, ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port))); // check device object: TCP or UDP if (irps->DeviceObject != g_tcpfltobj && irps->DeviceObject != g_udpfltobj) { KdPrint(("[tdi_fw] tdi_connect: unknown DeviceObject 0x%x!\n", irps->DeviceObject)); goto done; } ote_conn = ot_find_fileobj(irps->FileObject, &irql); if (ote_conn == NULL) { KdPrint(("[tdi_fw] tdi_connect: ot_find_fileobj(0x%x)!\n", irps->FileObject)); goto done; } if (get_original_devobj(irps->DeviceObject, &ipproto) == NULL || (ipproto != IPPROTO_TCP && ipproto != IPPROTO_UDP)) { // invalid device object! KdPrint(("[tdi_fw] tdi_connect: invalid device object 0x%x!\n", irps->DeviceObject)); goto done; } if (ipproto == IPPROTO_TCP) { /* * For TCP: get addrobj by connobj and get local address by it */ addrobj = ote_conn->associated_fileobj; if (addrobj == NULL) { KdPrint(("[tdi_fw] tdi_connect: empty addrobj!\n")); goto done; } ote_addr = ot_find_fileobj(addrobj, NULL); // we're already in spinlock if (ote_addr == NULL) { KdPrint(("[tdi_fw] tdi_connect: ot_find_fileobj(0x%x)!\n", addrobj)); goto done; } } else { /* * For UDP: connobj and addrobj are the same */ KdPrint(("[tdi_fw] tdi_connect: connected UDP socket detected\n")); // for connected UDP sockets connobj and addrobj are the same addrobj= irps->FileObject; ote_addr = ote_conn; } local_addr = (TA_ADDRESS *)(ote_addr->local_addr); // sanity check if (local_addr->AddressLength != remote_addr->AddressLength) { KdPrint(("[tdi_fw] tdi_connect: different addr lengths! (%u != %u)\n", local_addr->AddressLength, remote_addr->AddressLength)); goto done; } // set remote address with connobj if (remote_addr->AddressLength > sizeof(ote_conn->remote_addr)) { KdPrint(("[tdi_fw] tdi_connect: address too long! (%u)\n", remote_addr->AddressLength)); goto done; } memcpy(ote_conn->remote_addr, remote_addr, remote_addr->AddressLength); // set local address with connobj if (local_addr->AddressLength > sizeof(ote_conn->local_addr)) { KdPrint(("[tdi_fw] tdi_connect: address to long! (%u)\n", local_addr->AddressLength)); goto done; } memcpy(ote_conn->local_addr, local_addr, local_addr->AddressLength); KdPrint(("[tdi_fw] tdi_connect(pid:%u/%u): %x:%u -> %x:%u (ipproto = %d)\n", ote_conn->pid, PsGetCurrentProcessId(), ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port), ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port), ipproto)); /* * Call quick_filter */ request.struct_size = sizeof(request); request.type = TYPE_CONNECT; request.direction = DIRECTION_OUT; request.proto = ipproto; // don't use ote_conn->pid because one process can create connection object // but another one can connect request.pid = (ULONG)PsGetCurrentProcessId(); if (request.pid == 0) { // avoid idle process pid (XXX do we need this?) request.pid = ote_addr->pid; } // get user SID & attributes (can't call get_current_sid_a at DISPATCH_LEVEL) if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) request.sid_a_size = ote_addr->sid_a_size; memcpy(&request.addr.from, &local_addr->AddressType, sizeof(struct sockaddr)); memcpy(&request.addr.to, &remote_addr->AddressType, sizeof(struct sockaddr)); request.addr.len = sizeof(struct sockaddr_in); memset(&rule, 0, sizeof(rule)); result = quick_filter(&request, &rule); memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); if (result == FILTER_ALLOW && ipproto == IPPROTO_TCP) { struct flt_request *context_req = NULL; // add connection with state "SYN_SENT" status = add_tcp_conn(ote_conn, TCP_STATE_SYN_SENT); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_connect: add_conn: 0x%x!\n", status)); result = FILTER_DENY; goto done; // don't log this failure } if (rule.log >= RULE_LOG_LOG) { // set ote_conn->log_disconnect ote_conn->log_disconnect = (rule.log >= RULE_LOG_COUNT); // copy request for completion (LOG success or not) context_req = (struct flt_request *)malloc_np(sizeof(*context_req)); if (context_req != NULL) { memcpy(context_req, &request, sizeof(*context_req)); // don't free SID request.sid_a = NULL; // don't log request in this time rule.log = RULE_LOG_NOLOG; } } // set completion to add connection info to connection table completion->routine = tdi_connect_complete; completion->context = context_req; } // if logging is needed log request if (rule.log >= RULE_LOG_LOG) log_request(&request); done: // cleanup if (ote_conn != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); if (request.sid_a != NULL) free(request.sid_a); if (result != FILTER_ALLOW) { irp->IoStatus.Status = STATUS_REMOTE_NOT_LISTENING; // set fake status } return result; }
int tdi_set_event_handler(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) { PTDI_REQUEST_KERNEL_SET_EVENT r = (PTDI_REQUEST_KERNEL_SET_EVENT)&irps->Parameters; NTSTATUS status; struct ot_entry *ote_addr = NULL; KIRQL irql; int result = FILTER_DENY; TDI_EVENT_CONTEXT *ctx; KdPrint(("[tdi_fw] tdi_set_event_handler: [%s] devobj 0x%x; addrobj 0x%x; EventType: %d\n", r->EventHandler ? "(+)ADD" : "(-)REMOVE", irps->DeviceObject, irps->FileObject, r->EventType)); ote_addr = ot_find_fileobj(irps->FileObject, &irql); if (ote_addr == NULL) { KdPrint(("[tdi_fw] tdi_set_event_handler: ot_find_fileobj(0x%x)\n", irps->FileObject)); if (r->EventHandler == NULL) { // for fileobjects loaded earlier than our driver allow removing result = FILTER_ALLOW; } goto done; } if (r->EventType < 0 || r->EventType >= MAX_EVENT) { KdPrint(("[tdi_fw] tdi_set_event_handler: unknown EventType %d!\n", r->EventType)); result = FILTER_ALLOW; goto done; } ctx = &ote_addr->ctx[r->EventType]; if (r->EventHandler != NULL) { /* add EventHandler */ int i; for (i = 0; g_tdi_event_handlers[i].event != (ULONG)-1; i++) if (g_tdi_event_handlers[i].event == r->EventType) break; if (g_tdi_event_handlers[i].event == (ULONG)-1) { KdPrint(("[tdi_fw] tdi_set_event_handler: unknown EventType %d!\n", r->EventType)); result = FILTER_ALLOW; goto done; } ctx->old_handler = r->EventHandler; ctx->old_context = r->EventContext; if (g_tdi_event_handlers[i].handler != NULL) { r->EventHandler = g_tdi_event_handlers[i].handler; r->EventContext = ctx; } else { r->EventHandler = NULL; r->EventContext = NULL; } KdPrint(("[tdi_fw] tdi_set_event_handler: old_handler 0x%x; old_context 0x%x\n", r->EventHandler, r->EventContext)); } else { /* remove EventHandler */ ctx->old_handler = NULL; ctx->old_context = NULL; } // change LISTEN state if (r->EventType == TDI_EVENT_CONNECT) { TA_ADDRESS *local_addr; if (r->EventHandler != NULL) { // add "LISTEN" info status = add_listen(ote_addr); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_set_event_handler: add_listen: 0x%x!\n", status)); goto done; } } else if (ote_addr->listen_entry != NULL) { // remove "LISTEN" info del_listen_obj(ote_addr->listen_entry, FALSE); ote_addr->listen_entry = NULL; } // log it if address is not 127.0.0.1 local_addr = (TA_ADDRESS *)(ote_addr->local_addr); if (ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr) != 0x7f000001) { struct flt_request request; memset(&request, 0, sizeof(request)); request.struct_size = sizeof(request); request.type = (r->EventHandler != NULL) ? TYPE_LISTEN : TYPE_NOT_LISTEN; request.proto = IPPROTO_TCP; // correct? if (r->EventHandler != NULL) { // for removing event handler ProcessNotifyProc can be already called request.pid = (ULONG)PsGetCurrentProcessId(); if (request.pid == 0) { // avoid idle process pid (XXX do we need this?) request.pid = ote_addr->pid; } } else request.pid = (ULONG)-1; // get user SID & attributes (can't call get_current_sid_a at DISPATCH_LEVEL) if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) request.sid_a_size = ote_addr->sid_a_size; memcpy(&request.addr.from, &local_addr->AddressType, sizeof(struct sockaddr)); request.addr.len = sizeof(struct sockaddr_in); log_request(&request); if (request.sid_a != NULL) free(request.sid_a); } } result = FILTER_ALLOW; done: // cleanup if (ote_addr != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); return result; }
NTSTATUS tdi_event_receive( IN PVOID TdiEventContext, IN CONNECTION_CONTEXT ConnectionContext, IN ULONG ReceiveFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *BytesTaken, IN PVOID Tsdu, OUT PIRP *IoRequestPacket) { TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; PFILE_OBJECT connobj = ot_find_conn_ctx(ctx->fileobj, ConnectionContext); NTSTATUS status; KdPrint(("[tdi_fw] tdi_event_receive: addrobj 0x%x; connobj: 0x%x; %u/%u; flags: 0x%x\n", ctx->fileobj, connobj, BytesIndicated, BytesAvailable, ReceiveFlags)); status = ((PTDI_IND_RECEIVE)(ctx->old_handler)) (ctx->old_context, ConnectionContext, ReceiveFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, IoRequestPacket); KdPrint(("[tdi_fw] tdi_event_receive: status 0x%x; BytesTaken: %u; Irp: 0x%x\n", status, *BytesTaken, *IoRequestPacket)); if (*BytesTaken != 0) { struct ot_entry *ote_conn; KIRQL irql; ote_conn = ot_find_fileobj(connobj, &irql); if (ote_conn != NULL) { ULONG bytes = *BytesTaken; ote_conn->bytes_in += bytes; // traffic stats KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); g_traffic[TRAFFIC_TOTAL_IN] += bytes; if (ote_conn->log_disconnect) g_traffic[TRAFFIC_COUNTED_IN] += bytes; KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); KeReleaseSpinLock(&g_ot_hash_guard, irql); } } if (*IoRequestPacket != NULL) { // got IRP. replace completion. struct tdi_client_irp_ctx *new_ctx; PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(*IoRequestPacket); new_ctx = (struct tdi_client_irp_ctx *)malloc_np(sizeof(*new_ctx)); if (new_ctx != NULL) { new_ctx->connobj = connobj; if (irps->CompletionRoutine != NULL) { new_ctx->completion = irps->CompletionRoutine; new_ctx->context = irps->Context; new_ctx->old_control = irps->Control; } else { // we don't use IoSetCompletionRoutine because it uses next not current location new_ctx->completion = NULL; new_ctx->context = NULL; } irps->CompletionRoutine = tdi_client_irp_complete; irps->Context = new_ctx; irps->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; } } return status; }
NTSTATUS tdi_client_irp_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { struct tdi_client_irp_ctx *ctx = (struct tdi_client_irp_ctx *)Context; NTSTATUS status; KdPrint(("[tdi_fw] tdi_client_irp_complete: status: 0x%x; len: %u\n", Irp->IoStatus.Status, Irp->IoStatus.Information)); if (Irp->IoStatus.Status == STATUS_SUCCESS) { struct ot_entry *ote_conn; KIRQL irql; ote_conn = ot_find_fileobj(ctx->connobj, &irql); if (ote_conn != NULL) { ULONG bytes = Irp->IoStatus.Information; ote_conn->bytes_in += bytes; // traffic stats KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); g_traffic[TRAFFIC_TOTAL_IN] += bytes; if (ote_conn->log_disconnect) g_traffic[TRAFFIC_COUNTED_IN] += bytes; KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); KeReleaseSpinLock(&g_ot_hash_guard, irql); } } // call original completion if (ctx->completion != NULL) { // call old completion (see the old control) BOOLEAN b_call = FALSE; if (Irp->Cancel) { // cancel if (ctx->old_control & SL_INVOKE_ON_CANCEL) b_call = TRUE; } else { if (Irp->IoStatus.Status >= STATUS_SUCCESS) { // success if (ctx->old_control & SL_INVOKE_ON_SUCCESS) b_call = TRUE; } else { // error if (ctx->old_control & SL_INVOKE_ON_ERROR) b_call = TRUE; } } if (b_call) { status = (ctx->completion)(DeviceObject, Irp, ctx->context); KdPrint(("[tdi_flt] tdi_client_irp_complete: original handler: 0x%x; status: 0x%x\n", ctx->completion, status)); } else status = STATUS_SUCCESS; } free(ctx); return status; }
NTSTATUS tdi_event_receive_datagram( IN PVOID TdiEventContext, IN LONG SourceAddressLength, IN PVOID SourceAddress, IN LONG OptionsLength, IN PVOID Options, IN ULONG ReceiveDatagramFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *BytesTaken, IN PVOID Tsdu, OUT PIRP *IoRequestPacket) { TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; struct ot_entry *ote_addr = NULL; KIRQL irql; TA_ADDRESS *remote_addr, *local_addr; NTSTATUS status; int ipproto, result = FILTER_DENY; struct flt_request request; struct flt_rule rule; // get local address of address object memset(&request, 0, sizeof(request)); ote_addr = ot_find_fileobj(ctx->fileobj, &irql); if (ote_addr == NULL) { KdPrint(("[tdi_fw] tdi_receive_datagram: ot_find_fileobj(0x%x)!\n", ctx->fileobj)); goto done; } KdPrint(("[tdi_fw] tdi_event_receive_datagram: addrobj 0x%x\n", ctx->fileobj)); // check device object: UDP or RawIP if (get_original_devobj(ote_addr->devobj, &ipproto) == NULL || (ipproto != IPPROTO_UDP && ipproto != IPPROTO_IP)) { // unknown device object! KdPrint(("[tdi_fw] tdi_event_receive_datagram: unknown DeviceObject 0x%x!\n", ote_addr)); goto done; } local_addr = (TA_ADDRESS *)(ote_addr->local_addr); remote_addr = ((TRANSPORT_ADDRESS *)SourceAddress)->Address; KdPrint(("[tdi_fw] tdi_event_receive_datagram(pid:%u): %x:%u -> %x:%u\n", ote_addr->pid, ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port), ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port))); // call quick filter for datagram request.struct_size = sizeof(request); request.type = TYPE_DATAGRAM; request.direction = DIRECTION_IN; request.proto = ipproto; request.pid = ote_addr->pid; // get user SID & attributes (can't call get_current_sid_a at DISPATCH_LEVEL) if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) request.sid_a_size = ote_addr->sid_a_size; memcpy(&request.addr.from, &remote_addr->AddressType, sizeof(struct sockaddr)); memcpy(&request.addr.to, &local_addr->AddressType, sizeof(struct sockaddr)); request.addr.len = sizeof(struct sockaddr_in); memset(&rule, 0, sizeof(rule)); result = quick_filter(&request, &rule); memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); if (rule.log >= RULE_LOG_LOG) { ULONG bytes = BytesAvailable; // traffic stats KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); g_traffic[TRAFFIC_TOTAL_IN] += bytes; if (rule.log >= RULE_LOG_COUNT) { request.log_bytes_in = bytes; g_traffic[TRAFFIC_COUNTED_IN] += bytes; } else request.log_bytes_in = (ULONG)-1; KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); log_request(&request); } done: // cleanup if (ote_addr != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); if (request.sid_a != NULL) free(request.sid_a); if (result == FILTER_ALLOW) { return ((PTDI_IND_RECEIVE_DATAGRAM)(ctx->old_handler)) (ctx->old_context, SourceAddressLength, SourceAddress, OptionsLength, Options, ReceiveDatagramFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, IoRequestPacket); } else return STATUS_DATA_NOT_ACCEPTED; }