void * __bro_sobject_data_get(BroSObject *obj, const char *key) { void *result; if (! obj || ! key || ! *key) return NULL; result = __bro_ht_get(obj->data, (void *) key); /* D(("Retrieving data item '%s' from object %p yields %p\n", key, obj, result)); */ return result; }
int __bro_io_process_input(BroConn *bc) { uint32 buf_off, chunk_size; BroMsgHeader msg_hdr; int result = FALSE; D_ENTER; /* Read all available data into receive buffer. Our socket is * nonblocking so if nothing's available we'll be back right * away. If nothing was read, the subsequent for loop will exit * right away, so the io_msg_fill_rx() return code need not be * checked here. */ io_msg_fill_rx(bc); /* Try to process as much in the input buffer as we can */ for ( ; ; ) { D(("----- Attempting to extract a message\n")); /* Get the current offset of the buffer pointer to make * sure we can reset to it if things go wrong. */ buf_off = __bro_buf_ptr_tell(bc->rx_buf); /* Now check the buffer contents and see if there's enough * for us to analyze it. Start with a uint32 for the size * of the first chunk, and then the chunk itself. */ if (! io_read_chunk_size(bc, &chunk_size)) goto reset_return; if (chunk_size != sizeof(BroMsgHeader)) { D(("Received chunk should be %i bytes, but is %i\n", sizeof(BroMsgHeader), chunk_size)); io_skip_chunk(bc->rx_buf, buf_off, chunk_size); result = TRUE; continue; } if (! io_read_msg_hdr(bc, &msg_hdr)) goto reset_return; switch (msg_hdr.hdr_type) { case BRO_MSG_REQUEST: { char *tmp = NULL, *tmp2 = NULL; D(("Received MSQ_REQUEST\n")); /* We need to read another chunk, whose data will contain * a sequence of 0-terminated strings, each one being the * name of an event that the peering Bro is interested in. */ if (! io_read_chunk_size(bc, &chunk_size)) goto reset_return; if (! (tmp = (char *) malloc(chunk_size * sizeof(char)))) goto reset_return; if (! __bro_buf_read_data(bc->rx_buf, tmp, chunk_size)) { free(tmp); goto reset_return; } for (tmp2 = tmp; tmp2 < tmp + chunk_size; tmp2 = tmp2 + strlen(tmp2) + 1) { char *key; if (__bro_ht_get(bc->ev_mask, tmp2)) continue; key = strdup(tmp2); __bro_ht_add(bc->ev_mask, key, key); D(("Will report event '%s'\n", tmp2)); } D(("Now reporting %i event(s).\n", __bro_ht_get_size(bc->ev_mask))); free(tmp); } break; case BRO_MSG_VERSION: { uchar *data; uint32 proto_version; uint32 cache_size; uint32 data_version; uint32 runtime; /* unused */ D(("Received MSG_VERSION\n")); /* We need to read another chunk for the raw data. */ if (! io_read_chunk_size(bc, &chunk_size)) goto reset_return; if (! (data = malloc(sizeof(uchar) * chunk_size))) goto reset_return; if (! __bro_buf_read_data(bc->rx_buf, data, chunk_size)) { free(data); goto reset_return; } proto_version = ntohl(((uint32 *) data)[0]); cache_size = ntohl(((uint32 *) data)[1]); data_version = ntohl(((uint32 *) data)[2]); runtime = ntohl(((uint32 *) data)[3]); /* If there are more bytes than required for the 4 uint32s * used above, it means that the peer has sent a connection class * identifier. Extract and register in the handle. */ if (chunk_size > 4 * sizeof(uint32)) { if (bc->peer_class) free(bc->peer_class); bc->peer_class = strdup((char *) (data + 4 * sizeof(uint32))); } if (proto_version != BRO_PROTOCOL_VERSION) { D(("EEEK -- we speak protocol version %i, peer speeks %i. Aborting.\n", BRO_PROTOCOL_VERSION, proto_version)); __bro_openssl_shutdown(bc); free(data); goto reset_return; } else { D(("Protocols compatible, we speak version %i\n", BRO_PROTOCOL_VERSION)); } if (data_version != 0 && data_version != BRO_DATA_FORMAT_VERSION) { D(("EEEK -- we speak data format version %i, peer speeks %i. Aborting.\n", BRO_DATA_FORMAT_VERSION, data_version)); __bro_openssl_shutdown(bc); free(data); goto reset_return; } else { D(("Data formats compatible, we speak version %i\n", BRO_DATA_FORMAT_VERSION)); } bc->io_cache_maxsize = cache_size; D(("Receiver cache size set to %i entries.\n", cache_size)); free(data); bc->state->conn_state_peer = BRO_CONNSTATE_HANDSHAKE; D(("VERSION received, on %p, peer now in HANDSHAKE stage.\n")); } break; case BRO_MSG_SERIAL: { uint32 pre_serial; D(("Received MSQ_SERIAL\n")); pre_serial = __bro_buf_ptr_tell(bc->rx_buf); if (! io_read_chunk_size(bc, &chunk_size)) goto reset_return; if (! io_process_serialization(bc)) io_skip_chunk(bc->rx_buf, pre_serial, chunk_size); } break; case BRO_MSG_CAPTURE_FILTER: { uint32 pre_capture; D(("Received MSQ_CAPTURE_FILTER\n")); pre_capture = __bro_buf_ptr_tell(bc->rx_buf); if (! io_read_chunk_size(bc, &chunk_size)) goto reset_return; io_skip_chunk(bc->rx_buf, pre_capture, chunk_size); } break; case BRO_MSG_PHASE_DONE: /* No additional content for this one. */ switch (bc->state->conn_state_peer) { case BRO_CONNSTATE_HANDSHAKE: /* When we complete the handshake phase, it depends * on whether or not the peer has requested synced * state. If so, enter the sync phase, otherwise * we're up and running. */ if (bc->state->sync_state_requested) { bc->state->conn_state_peer = BRO_CONNSTATE_SYNC; D(("Phase done from peer on %p, sync requested, peer now in SYNC stage.\n", bc)); } else { bc->state->conn_state_peer = BRO_CONNSTATE_RUNNING; D(("Phase done from peer on %p, no sync requested, peer now in RUNNING stage.\n", bc)); } break; case BRO_CONNSTATE_SYNC: bc->state->conn_state_peer = BRO_CONNSTATE_RUNNING; D(("Phase done from peer on %p, peer now in RUNNING stage.\n", bc)); break; default: D(("Ignoring PHASE_DONE in conn state %i/%i on conn %p\n", bc->state->conn_state_self, bc->state->conn_state_peer, bc)); } break; case BRO_MSG_REQUEST_SYNC: { uchar *data; D(("Received MSQ_REQUEST_SYNC, peer now in SYNC stage.\n")); bc->state->sync_state_requested = 1; bc->state->conn_state_peer = BRO_CONNSTATE_SYNC; /* We need to read another chunk for the raw data. */ if (! io_read_chunk_size(bc, &chunk_size)) goto reset_return; if (! (data = malloc(sizeof(uchar) * chunk_size))) goto reset_return; if (! __bro_buf_read_data(bc->rx_buf, data, chunk_size)) { free(data); goto reset_return; } D(("Skipping sync interpretation\n")); free(data); break; } case BRO_MSG_CAPS: { uchar *data; D(("Received MSG_CAPS\n")); /* We need to read another chunk for the raw data. */ if (! io_read_chunk_size(bc, &chunk_size)) goto reset_return; if (! (data = malloc(sizeof(uchar) * chunk_size))) goto reset_return; if (! __bro_buf_read_data(bc->rx_buf, data, chunk_size)) { free(data); goto reset_return; } D(("Skipping capabilities interpretation\n")); free(data); break; } default: D(("Skipping unknown message type %i\n", msg_hdr.hdr_type)); break; } __bro_buf_consume(bc->rx_buf); result = TRUE; if ((bc->conn_flags & BRO_CFLAG_YIELD) && bc->state->conn_state_self == BRO_CONNSTATE_RUNNING && bc->state->conn_state_peer == BRO_CONNSTATE_RUNNING) break; } reset_return: __bro_buf_ptr_seek(bc->rx_buf, buf_off, SEEK_SET); D_RETURN_(result); }
static int io_msg_fill_tx(BroConn *bc, BroMsg *msg) { int result = TRUE; D_ENTER; if (!bc || !msg) { D(("Input error.\n")); D_RETURN_(FALSE); } /* Check if anything is still left in the input buffer. In that case, * we don't fill anything in but return right away, so the message * gets queued. */ if (__bro_buf_get_used_size(bc->tx_buf) > 0) { D(("Buffer not empty; not filling!\n")); D_RETURN_(FALSE); } D((">>> Attempting write of %s\n", msg_type_2_str(msg->msg_header.hdr_type))); /* We will collect the message chunk in the connection's tx buffer. * We append stuff to it as we go along and at the end write it out. * When being sent, he buffer has the amount of octets to send at * the beginning, so the reader knows how much is coming. */ __bro_buf_reset(bc->tx_buf); msg->msg_header_size = sizeof(BroMsgHeader); if (! __bro_buf_write_int(bc->tx_buf, msg->msg_header_size)) { __bro_buf_reset(bc->tx_buf); D_RETURN_(FALSE); } /* Hook in the Bro message header */ if (! io_fill_msg_header(bc->tx_buf, &msg->msg_header)) { __bro_buf_reset(bc->tx_buf); D_RETURN_(FALSE); } if (msg->msg_cont_type != BRO_MSG_CONT_NONE) { uint32 msg_size_pos, msg_size_end; /* This starts another chunk of data (in Bro protocol speak), * but here we cannot yet know how big the chunk will be. * We save the offset in the buffer and return to it later, * overwriting the value with the then correct one. */ msg_size_pos = __bro_buf_get_used_size(bc->tx_buf); if (! __bro_buf_write_int(bc->tx_buf, msg->msg_size)) { __bro_buf_reset(bc->tx_buf); D_RETURN_(FALSE); } /* Gather the payload of the message we are about * to send into the buffer BUF. */ switch (msg->msg_cont_type) { case BRO_MSG_CONT_RAW: D(("Filling raw data into buffer\n")); if (! io_fill_raw(bc->tx_buf, msg->msg_cont_raw)) { __bro_buf_reset(bc->tx_buf); D_RETURN_(FALSE); } break; case BRO_MSG_CONT_EVENT: /* Check if the peer actually requested the event, and if not, * drop it silently (i.e., still return success). */ if (! __bro_ht_get(bc->ev_mask, msg->msg_cont_ev->name.str_val)) { D(("Event '%s' not requested by peer -- dropping.\n", msg->msg_cont_ev->name.str_val)); __bro_buf_reset(bc->tx_buf); /* This is not an error but a silent drop, so we * return success. */ D_RETURN_(TRUE); } D(("Filling event into buffer\n")); if (! __bro_event_serialize(msg->msg_cont_ev, bc)) { D(("Error during serialization.\n")); __bro_buf_reset(bc->tx_buf); D_RETURN_(FALSE); } break; case BRO_MSG_CONT_REQUEST: D(("Filling request into buffer\n")); if (! io_fill_request(bc->tx_buf, msg->msg_cont_req)) { __bro_buf_reset(bc->tx_buf); D_RETURN_(FALSE); } break; #ifdef BRO_PCAP_SUPPORT case BRO_MSG_CONT_PACKET: if (! __bro_packet_serialize(msg->msg_cont_packet, bc)) { __bro_buf_reset(bc->tx_buf); D_RETURN_(FALSE); } break; #endif default: D(("ERROR -- invalid message content code %i\n", msg->msg_cont_type)); break; } /* Now calculate length of entire transmission -- * we know where we wrote the uint32 containing the * size of the chunk, and we know where we are now, * so the length is their difference, minus the uint32 * itself. */ msg_size_end = __bro_buf_get_used_size(bc->tx_buf); msg->msg_size = msg_size_end - msg_size_pos - sizeof(uint32); D(("Serialized message sized %i bytes.\n", msg->msg_size)); if (! __bro_buf_ptr_seek(bc->tx_buf, msg_size_pos, SEEK_SET)) { D(("Cannot seek to position %u -- we're screwed.\n", msg_size_pos)); __bro_buf_reset(bc->tx_buf); D_RETURN_(FALSE); } if (! __bro_buf_write_int(bc->tx_buf, msg->msg_size)) { __bro_buf_reset(bc->tx_buf); D_RETURN_(FALSE); } } D_RETURN_(result); }
BroSObject * __bro_sobject_unserialize(uint16 type_id_wanted, BroConn *bc) { BroSObject *obj; char full_obj; uint32 perm_id; uint16 type_id; D_ENTER; if (! bc) D_RETURN_(NULL); /* Same special case for types as in __bro_sobject_serialize(). */ if ( (type_id_wanted & SER_TYPE_MASK) == SER_IS_TYPE) { D(("Unserializing a type, checking for name-only format.\n")); if (! __bro_buf_read_char(bc->rx_buf, &full_obj)) D_RETURN_(NULL); if (! full_obj) { BroString tmp; bro_string_init(&tmp); /* We only get the name. */ if (! __bro_buf_read_string(bc->rx_buf, &tmp)) D_RETURN_(FALSE); /* We don't really have a namespace in which we can now * look up the type, so there's not much we can do! */ D(("Received name-only type '%s', reporting failure.\n", bro_string_get_data(&tmp))); D_RETURN_(FALSE); } } if (! __bro_buf_read_char(bc->rx_buf, &full_obj)) D_RETURN_(NULL); if (! __bro_buf_read_int(bc->rx_buf, &perm_id)) D_RETURN_(NULL); if (! full_obj) { #ifdef BRO_DEBUG if (! (bc->conn_flags & BRO_CFLAG_CACHE)) D(("WARNING: no caching requested, yet peer sends cached data.\n")); #endif if (! (obj = __bro_ht_get(bc->io_cache, (void *) perm_id))) { D(("Cache inconsistency: cache should contain object %i\n", perm_id)); D_RETURN_(NULL); } __bro_sobject_ref(obj); D(("Returning object %i/%p from cache.\n", perm_id, obj)); D_RETURN_(obj); } if (! __bro_buf_read_short(bc->rx_buf, &type_id)) D_RETURN_(NULL); /* Now check if the stuff that's arriving is actually an * instance of the type we'd like to see -- we can only do * primitive checking for inherited types (when we want to * know that it's a type, say, but we cannot know what exact * kind of type) -- so we just check whether all the bits set * in both type id's match: */ if ((type_id_wanted & SER_TYPE_MASK) != (type_id & SER_TYPE_MASK)) { D(("Type mismatch in serialization: wanted %04x, got %04x.\n", type_id_wanted, type_id)); D_RETURN_(NULL); } if (! (obj = __bro_sobject_create(type_id))) D_RETURN_(NULL); /* Polymorphism: depending on the constructor of the object, * this call will start from the bottom of the hierarchy and * read members in step by step, so by the time we return * from this function the object is fully unserialized. */ if (! obj->read(obj, bc)) { D(("Reading object %i of type 0x%04x FAILED.\n", perm_id, type_id)); __bro_sobject_release(obj); D_RETURN_(NULL); } /* If we have asked the peer to use caching, * make sure the object is in the cache: */ if ( (bc->conn_flags & BRO_CFLAG_CACHE) && ! __bro_ht_get(bc->io_cache, (void *) perm_id)) { D(("Storing object %i in cache.\n", perm_id)); __bro_ht_add(bc->io_cache, (void *) perm_id, obj); obj->perm_id = perm_id; __bro_sobject_ref(obj); } D(("Object %i of type 0x%04x unserialized successfully.\n", perm_id, type_id)); D_RETURN_(obj); }