Beispiel #1
0
int proto_mpeg_sect_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) {

    struct proto_process_stack *s = &stack[stack_index];
    struct proto_process_stack *s_next = &stack[stack_index + 1];

    unsigned char *buff = s->pload;

    if (s->plen < 3)
        return PROTO_INVALID;

    unsigned int len = (((buff[1] & 0xF) << 8) | buff[2]) + 3;
    if (len > s->plen)
        return PROTO_INVALID;

    PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_mpeg_sect_field_table_id], buff[0]);

    // We usually pass the whole payload including the table_id
    switch (buff[0]) {
    case 0x3E: // ETSI EN 301 192 | ISO 13818-6 (DVB MPE)
        s_next->proto = proto_mpeg_dvb_mpe;
        s_next->pload = s->pload;
        s_next->plen = s->plen;
        break;
    }


    return PROTO_OK;

}
Beispiel #2
0
static int proto_vlan_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) {

	struct proto_process_stack *s = &stack[stack_index];

	if (s->plen < PROTO_VLAN_HEADER_SIZE)
		return PROTO_INVALID;

	uint16_t tci = ntohs(*(uint16_t*)s->pload);
	uint16_t *ether_type = s->pload + sizeof(uint16_t);


	uint16_t de = (tci & PROTO_VLAN_DE_MASK) >> PROTO_VLAN_DE_SHIFT;
	uint16_t pcp = (tci & PROTO_VLAN_PCP_MASK) >> PROTO_VLAN_PCP_SHIFT;
	uint16_t vid = tci & PROTO_VLAN_VID_MASK;

	
	PTYPE_UINT16_SETVAL(s->pkt_info->fields_value[proto_vlan_field_vid], vid);
	PTYPE_BOOL_SETVAL(s->pkt_info->fields_value[proto_vlan_field_de], de);
	PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_vlan_field_pcp], pcp);

	struct proto_process_stack *s_next = &stack[stack_index + 1];

	s_next->pload = s->pload + PROTO_VLAN_HEADER_SIZE;
	s_next->plen = s->plen - PROTO_VLAN_HEADER_SIZE;

	s_next->proto = proto_get_by_number(s->proto, ntohs(*ether_type));

	return PROTO_OK;

}
Beispiel #3
0
int registry_config_save(char *config_name) {

	if (strlen(config_name) >= REGISTRY_CONFIG_NAME_MAX) {
		pomlog(POMLOG_ERR "Configuration name too long, max %u characters.", REGISTRY_CONFIG_NAME_MAX);
		return POM_ERR;
	}

	struct dataset_query *dsq_config_list = NULL, *dsq_config = NULL;
	
	struct datastore *sys_dstore = system_datastore();
	if (!sys_dstore)
		return POM_ERR;

	struct datastore_connection *dc = datastore_connection_new(sys_dstore);
	if (!dc)
		return POM_ERR;

	dsq_config_list = datastore_dataset_query_open(sys_dstore, REGISTRY_CONFIG_LIST, registry_config_list_dataset_template, dc);
	if (!dsq_config_list)
		goto err;

	if (datastore_dataset_query_set_string_condition(dsq_config_list, 0, PTYPE_OP_EQ, config_name) != POM_OK)
		goto err;

	dsq_config = datastore_dataset_query_open(sys_dstore, REGISTRY_CONFIG, registry_config_dataset_template, dc);
	if (!dsq_config)
		goto err;

	if (datastore_transaction_begin(dc) != POM_OK)
		goto err;

	// Find out if we already have a config by that name
	int res = datastore_dataset_read_single(dsq_config_list);
	if (res == DATASET_QUERY_MORE) {

		// Delete existing stuff about this config
		if (datastore_dataset_query_set_uint64_condition(dsq_config, 0, PTYPE_OP_EQ, dsq_config_list->data_id) != POM_OK)
			goto err;

		if (datastore_dataset_delete(dsq_config_list) != DATASET_QUERY_OK)
			goto err;

		if (datastore_dataset_delete(dsq_config) != DATASET_QUERY_OK)
			goto err;
	}

	if (res < 0)
		goto err;

	// Add the config to the config list
	PTYPE_STRING_SETVAL(dsq_config_list->values[0].value, config_name);
	PTYPE_TIMESTAMP_SETVAL(dsq_config_list->values[1].value, pom_gettimeofday());

	if (datastore_dataset_write(dsq_config_list) != DATASET_QUERY_OK)
		goto err;



	PTYPE_UINT64_SETVAL(dsq_config->values[0].value, dsq_config_list->data_id);

	registry_lock();
	struct registry_class *cls;

	// Browse each class
	for (cls = registry_head; cls; cls = cls->next) {

		// Browse each instance of the class
		struct registry_instance *inst;
		for (inst = cls->instances; inst; inst = inst->next) {
			
			// Don't add the instance if it's not added by the user
			
			if (cls->instance_add) {

				// The system datastore will always exist
				if (inst == sys_dstore->reg_instance)
					continue;

				char *buff = malloc(strlen(cls->name) + 1 + strlen(inst->name) + 1);
				if (!buff) {
					pom_oom(strlen(cls->name) + 1 + strlen(inst->name) + 1);
					goto err_locked;
				}

				strcpy(buff, cls->name);
				strcat(buff, ".");
				strcat(buff, inst->name);
				PTYPE_STRING_SETVAL_P(dsq_config->values[1].value, buff);

				struct registry_param *p;
				for (p = inst->params; p && strcmp(p->name, "type"); p = p->next);

				if (p) {
					dsq_config->values[2].is_null = 0;
					char *type = PTYPE_STRING_GETVAL(p->value);
					PTYPE_STRING_SETVAL(dsq_config->values[2].value, type);
				} else {
					dsq_config->values[2].is_null = 1;
				}

				PTYPE_UINT8_SETVAL(dsq_config->values[3].value, registry_config_instance);

				if (datastore_dataset_write(dsq_config) != DATASET_QUERY_OK)
					goto err_locked;

			}

			// Browse the parametrers and add the non default ones

			struct registry_param *param;
			for (param = inst->params; param; param = param->next) {

				// Check if the parameter value is not the default one anymore
				if (param->default_value) {
					struct ptype *defval = ptype_alloc_from(param->value);
					if (!defval)
						goto err_locked;

					if (ptype_parse_val(defval, param->default_value) != POM_OK) {
						pomlog(POMLOG_ERR "Unable to parse default value !");
						ptype_cleanup(defval);
						goto err_locked;
					}

					if (ptype_compare_val(PTYPE_OP_EQ, param->value, defval)) {
						// Param still has the default value, do nothing
						ptype_cleanup(defval);
						continue;
					}

					ptype_cleanup(defval);
				}

				char *buff = malloc(strlen(cls->name) + 1 + strlen(inst->name) + 1 + strlen(param->name) + 1);
				if (!buff) {
					pom_oom(strlen(cls->name) + 1 + strlen(inst->name) + 1 + strlen(param->name) + 1);
					goto err_locked;
				}
				strcpy(buff, cls->name);
				strcat(buff, ".");
				strcat(buff, inst->name);
				strcat(buff, ".");
				strcat(buff, param->name);
				PTYPE_STRING_SETVAL_P(dsq_config->values[1].value, buff);

				char *value = ptype_print_val_alloc(param->value, NULL);
				if (!value)
					goto err_locked;
				
				dsq_config->values[2].is_null = 0;
				PTYPE_STRING_SETVAL_P(dsq_config->values[2].value, value);

				PTYPE_UINT8_SETVAL(dsq_config->values[3].value, registry_config_instance_param);

				if (datastore_dataset_write(dsq_config) != DATASET_QUERY_OK)
					goto err_locked;
			}
		
		}

	}

	registry_config_serial++;
	registry_serial++;
	xmlrcpcmd_serial_inc();

	registry_unlock();

	if (datastore_transaction_commit(dc) != POM_OK)
		goto err;

	datastore_dataset_query_cleanup(dsq_config_list);
	datastore_dataset_query_cleanup(dsq_config);
	
	datastore_connection_release(dc);

	pomlog("Registry configuration saved as \"%s\"", config_name);

	return POM_OK;

err_locked:
	registry_unlock();

err:
	if (dsq_config_list)
		datastore_dataset_query_cleanup(dsq_config_list);

	if (dsq_config)
		datastore_dataset_query_cleanup(dsq_config);

	if (dc) {
		datastore_transaction_rollback(dc);
		datastore_connection_release(dc);
	}

	return POM_ERR;

}
Beispiel #4
0
static int proto_eap_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) {

	struct proto_process_stack *s = &stack[stack_index];

	if (sizeof(struct eap_header) > s->plen)
		return PROTO_INVALID;

	struct proto_eap_priv *priv = proto_priv;

	struct eap_header *hdr = s->pload;

	PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_eap_field_code], hdr->code);
	PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_eap_field_identifier], hdr->identifier);

	
	if (hdr->code < 1 || hdr->code > 4)
		return PROTO_INVALID;

	uint16_t len = ntohs(hdr->length);

	if (len > s->plen)
		return PROTO_INVALID;

	// Keep only the payload lenght
	len -= sizeof(struct eap_header);
	
	if (conntrack_get(stack, stack_index) != POM_OK)
		return PROTO_ERR;

	if (conntrack_delayed_cleanup(s->ce, *PTYPE_UINT32_GETVAL(priv->p_timeout), p->ts) != POM_OK) {
		conntrack_unlock(s->ce);
		return PROTO_ERR;
	}
	conntrack_unlock(s->ce);

	if (hdr->code == 3 || hdr->code == 4) {
		// Content length is 0 for success and failure
		if (len != 4)
			return PROTO_INVALID;
		len = 0;

		if (!event_has_listener(priv->evt_success_failure))
			return PROTO_OK;

		struct event *evt = event_alloc(priv->evt_success_failure);
		if (!evt)
			return PROTO_ERR;
		struct data *evt_data = event_get_data(evt);
		PTYPE_UINT8_SETVAL(evt_data[evt_eap_common_identifier].value, hdr->identifier);
		data_set(evt_data[evt_eap_common_identifier]);
		PTYPE_BOOL_SETVAL(evt_data[evt_eap_success_failure_success].value, (hdr->code == 3 ? 1 : 0));
		data_set(evt_data[evt_eap_success_failure_success]);

		return event_process(evt, stack, stack_index, p->ts);
	}

	// At this point, code is either 1 or 2 (request/response)

	void *pload = s->pload + sizeof(struct eap_header);

	uint8_t type = 0;

	// There is at least 1 byte of data for request/response
	if (len < 1)
		return PROTO_INVALID;
	len--;

	type = *(uint8_t*)pload;
	pload++;

	struct event *evt = NULL;
	struct data *evt_data = NULL;
	
	switch (type) {
		case 1: // Identity
			
			if (!event_has_listener(priv->evt_identity))
				break;

			evt = event_alloc(priv->evt_identity);
			if (!evt)
				return PROTO_ERR;
			if (len) {
				evt_data = event_get_data(evt);
				PTYPE_STRING_SETVAL_N(evt_data[evt_eap_identity_identity].value, pload, len);
				data_set(evt_data[evt_eap_identity_identity]);
			}

			break;

		case 4: // MD5-Challenge
		
			if (!event_has_listener(priv->evt_md5_challenge))
				break;

			if (len < 17)
				return PROTO_INVALID;

			uint8_t value_size = *(uint8_t*)pload;
			if (value_size != 16)
				return PROTO_INVALID;

			pload++;
			len--;

			evt = event_alloc(priv->evt_md5_challenge);
			if (!evt)
				return PROTO_ERR;
			evt_data = event_get_data(evt);

			PTYPE_BYTES_SETLEN(evt_data[evt_eap_md5_challenge_value].value, 16);
			PTYPE_BYTES_SETVAL(evt_data[evt_eap_md5_challenge_value].value, pload);
			data_set(evt_data[evt_eap_md5_challenge_value]);
			
			if (len > 16) {
				PTYPE_STRING_SETVAL_N(evt_data[evt_eap_md5_challenge_name].value, pload + 16, len - 16);
				data_set(evt_data[evt_eap_md5_challenge_name]);
			}
			break;
	}
	
	if (evt) {
		if (!evt_data)
			evt_data = event_get_data(evt);
		PTYPE_UINT8_SETVAL(evt_data[evt_eap_common_identifier].value, hdr->identifier);
		data_set(evt_data[evt_eap_common_identifier]);
		PTYPE_UINT8_SETVAL(evt_data[evt_eap_common_code].value, hdr->code);
		data_set(evt_data[evt_eap_common_code]);

		if (event_process(evt, stack, stack_index, p->ts) != POM_OK)
			return PROTO_ERR;
	}


	return PROTO_OK;

}
Beispiel #5
0
static int proto_ipv4_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) {

	struct proto_process_stack *s = &stack[stack_index];
	struct proto_process_stack *s_next = &stack[stack_index + 1];

	struct ip* hdr = s->pload;

	unsigned int hdr_len = hdr->ip_hl * 4;

	if (s->plen < sizeof(struct ip) || // length smaller than header
		hdr->ip_hl < 5 || // ip header < 5 bytes
		ntohs(hdr->ip_len) < hdr_len || // datagram size < ip header length
		ntohs(hdr->ip_len) > s->plen) { // datagram size > given size
		return PROTO_INVALID;
	}


	PTYPE_IPV4_SETADDR(s->pkt_info->fields_value[proto_ipv4_field_src], hdr->ip_src);
	PTYPE_IPV4_SETADDR(s->pkt_info->fields_value[proto_ipv4_field_dst], hdr->ip_dst);
	PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ipv4_field_tos], hdr->ip_tos);
	PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ipv4_field_ttl], hdr->ip_ttl);

	// Handle conntrack stuff
	if (conntrack_get(stack, stack_index) != POM_OK)
		return PROTO_ERR;


	s_next->pload = s->pload + hdr_len;
	s_next->plen = ntohs(hdr->ip_len) - hdr_len;

	s_next->proto = proto_get_by_number(s->proto, hdr->ip_p);


	int res = POM_ERR;
	if (s->ce->children) {
		res = conntrack_delayed_cleanup(s->ce, 0, p->ts);
	} else {
		uint32_t *conntrack_timeout = PTYPE_UINT32_GETVAL(param_conntrack_timeout);
		res = conntrack_delayed_cleanup(s->ce, *conntrack_timeout, p->ts);
	}
	if (res == POM_ERR) {
		conntrack_unlock(s->ce);
		return PROTO_ERR;
	}

	uint16_t frag_off = ntohs(hdr->ip_off);

	// Check if packet is fragmented and need more handling

	if (frag_off & IP_DONT_FRAG) {
		conntrack_unlock(s->ce);
		return PROTO_OK; // Nothing to do
	}

	if (!(frag_off & IP_MORE_FRAG) && !(frag_off & IP_OFFSET_MASK)) {
		conntrack_unlock(s->ce);
		return PROTO_OK; // Nothing to do, full packet
	}

	uint16_t offset = (frag_off & IP_OFFSET_MASK) << 3;
	size_t frag_size = ntohs(hdr->ip_len) - (hdr->ip_hl * 4);

	// Ignore invalid fragments
	if (frag_size > 0xFFFF) {
		conntrack_unlock(s->ce);
		return PROTO_INVALID;
	}

	if (frag_size > s->plen + hdr_len) {
		conntrack_unlock(s->ce);
		return PROTO_INVALID;
	}

	// Account for one more fragment
	registry_perf_inc(perf_frags, 1);

	struct proto_ipv4_fragment *tmp = s->ce->priv;

	// Let's find the right buffer
	for (; tmp && tmp->id != hdr->ip_id; tmp = tmp->next);

	if (!tmp) {
		// Buffer not found, create it
		tmp = malloc(sizeof(struct proto_ipv4_fragment));
		if (!tmp) {
			pom_oom(sizeof(struct proto_ipv4_fragment));
			conntrack_unlock(s->ce);
			return PROTO_ERR;
		}
		memset(tmp, 0, sizeof(struct proto_ipv4_fragment));

		tmp->t = conntrack_timer_alloc(s->ce, proto_ipv4_fragment_cleanup, tmp);
		if (!tmp->t) {
			conntrack_unlock(s->ce);
			free(tmp);
			return PROTO_ERR;
		}
		
		tmp->id = hdr->ip_id;

		if (!s_next->proto) {
			// Set processed flag so no attempt to process this will be done
			tmp->flags |= PROTO_IPV4_FLAG_PROCESSED;
			conntrack_unlock(s->ce);
			conntrack_timer_cleanup(tmp->t);
			free(tmp);
			return PROTO_STOP;
		}

		tmp->multipart = packet_multipart_alloc(s_next->proto, 0);
		if (!tmp->multipart) {
			conntrack_unlock(s->ce);
			conntrack_timer_cleanup(tmp->t);
			free(tmp);
			return PROTO_ERR;
		}

		tmp->next = s->ce->priv;
		if (tmp->next)
			tmp->next->prev = tmp;
		s->ce->priv = tmp;
	}

	// Fragment was already handled
	if (tmp->flags & PROTO_IPV4_FLAG_PROCESSED) {
		conntrack_unlock(s->ce);
		registry_perf_inc(perf_frags_dropped, 1);
		return PROTO_STOP;
	}
	
	// Add the fragment
	if (packet_multipart_add_packet(tmp->multipart, p, offset, frag_size, (s->pload - (void*)p->buff) + (hdr->ip_hl * 4)) != POM_OK) {
		conntrack_unlock(s->ce);
		packet_multipart_cleanup(tmp->multipart);
		conntrack_timer_cleanup(tmp->t);
		free(tmp);
		return PROTO_ERR;
	}
	tmp->count++;

	// Schedule the timeout for the fragment
	uint32_t *frag_timeout = PTYPE_UINT32_GETVAL(param_frag_timeout);
	conntrack_timer_queue(tmp->t, *frag_timeout, p->ts);


	if (!(frag_off & IP_MORE_FRAG))
		tmp->flags |= PROTO_IPV4_FLAG_GOT_LAST;

	if ((tmp->flags & PROTO_IPV4_FLAG_GOT_LAST) && !tmp->multipart->gaps)
		tmp->flags |= PROTO_IPV4_FLAG_PROCESSED;


	conntrack_unlock(s->ce);
	
	if ((tmp->flags & PROTO_IPV4_FLAG_PROCESSED)) {
		int res = packet_multipart_process(tmp->multipart, stack, stack_index + 1);
		tmp->multipart = NULL; // Multipart will be cleared automatically
		if (res == PROTO_ERR) {
			return PROTO_ERR;
		} else if (res == PROTO_INVALID) {
			registry_perf_inc(perf_frags_dropped, tmp->count);
		} else {
			registry_perf_inc(perf_reassembled_pkts, 1);
		}
	}

	return PROTO_STOP; // Stop processing the packet

}
Beispiel #6
0
static int proto_ppp_pap_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) {

	struct proto_process_stack *s = &stack[stack_index];

	if (sizeof(struct ppp_pap_header) > s->plen)
		return PROTO_INVALID;

	struct ppp_pap_header *pchdr = s->pload;
	size_t len = ntohs(pchdr->length);

	if (len > s->plen)
		return PROTO_INVALID;

	// Keep only the payload len
	len -= sizeof(struct ppp_pap_header);

	PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ppp_pap_field_code], pchdr->code);
	PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ppp_pap_field_identifier], pchdr->identifier);

	struct proto_ppp_pap_priv *priv = proto_priv;

	if (conntrack_get(stack, stack_index) != POM_OK)
		return PROTO_ERR;
	if (conntrack_delayed_cleanup(s->ce, *PTYPE_UINT32_GETVAL(priv->p_auth_timeout), p->ts) != POM_OK) {
		conntrack_unlock(s->ce);
		return PROTO_ERR;
	}
	
	conntrack_unlock(s->ce);

	if (pchdr->code == 1 && event_has_listener(priv->evt_request)) {

		if (len < 4)
			return PROTO_INVALID;

		uint8_t *peer_id_len = s->pload + sizeof(struct ppp_pap_header);
		if (*peer_id_len > len - 2)
			return PROTO_INVALID;

		len -= (*peer_id_len + 1);
		uint8_t *pwd_len = peer_id_len + *peer_id_len + 1;
		if (*pwd_len > len - 1)
			return PROTO_INVALID;


		// Process the challenge/response event
		struct event *evt = event_alloc(priv->evt_request);
		if (!evt)
			return PROTO_ERR;

		struct data *evt_data = event_get_data(evt);
		PTYPE_UINT8_SETVAL(evt_data[evt_ppp_pap_request_code].value, pchdr->code);
		data_set(evt_data[evt_ppp_pap_request_code]);
		PTYPE_UINT8_SETVAL(evt_data[evt_ppp_pap_request_identifier].value, pchdr->identifier);
		data_set(evt_data[evt_ppp_pap_request_identifier]);

	
		PTYPE_STRING_SETVAL_N(evt_data[evt_ppp_pap_request_peer_id].value, (char *)peer_id_len + 1, *peer_id_len);
		data_set(evt_data[evt_ppp_pap_request_peer_id]);

		PTYPE_STRING_SETVAL_N(evt_data[evt_ppp_pap_request_password].value, (char *)pwd_len + 1, *pwd_len);
		data_set(evt_data[evt_ppp_pap_request_password]);

		if (event_process(evt, stack, stack_index, p->ts) != POM_OK)
			return PROTO_ERR;

	}

	if ((pchdr->code == 2 || pchdr->code == 3) && event_has_listener(priv->evt_ack_nack)) {
		
		struct event *evt = event_alloc(priv->evt_ack_nack);
		if (!evt)
			return PROTO_ERR;

		struct data *evt_data = event_get_data(evt);
		PTYPE_UINT8_SETVAL(evt_data[evt_ppp_pap_ack_nack_code].value, pchdr->code);
		data_set(evt_data[evt_ppp_pap_ack_nack_code]);
		PTYPE_UINT8_SETVAL(evt_data[evt_ppp_pap_ack_nack_identifier].value, pchdr->identifier);
		data_set(evt_data[evt_ppp_pap_ack_nack_identifier]);

		uint8_t *msg_len = s->pload + sizeof(struct ppp_pap_header);
		if (*msg_len > len - 1)
			return PROTO_INVALID;

		PTYPE_STRING_SETVAL_N(evt_data[evt_ppp_pap_ack_nack_message].value, (char *)msg_len + 1, *msg_len);
		data_set(evt_data[evt_ppp_pap_ack_nack_message]);

		if (event_process(evt, stack, stack_index, p->ts) != POM_OK)
			return PROTO_ERR;

	}

	return PROTO_OK;

}
Beispiel #7
0
static void analyzer_jpeg_exif_entry_analyze(ExifEntry *entry, void *pload) {

	ExifIfd ifd = exif_content_get_ifd(entry->parent);

	const char *tag_name = exif_tag_get_name_in_ifd(entry->tag, ifd);
	if (!tag_name) // Unknown tag
		return;

	struct ptype *value = NULL;
	// First parse ascii values
	if (entry->format == EXIF_FORMAT_ASCII) {
		char *str = malloc(entry->size);
		if (!str) {
			pom_oom(entry->size);
			return;
		}
		memcpy(str, entry->data, entry->size);
		// Make sure it's NULL terminated
		str[entry->size - 1] = 0;

		value = ptype_alloc("string");
		if (!value) {
			free(str);
			return;
		}
		PTYPE_STRING_SETVAL_P(value, str);
	} else if (entry->components == 1) {
		
		ExifByteOrder byte_order = exif_data_get_byte_order(entry->parent->parent);
		if (entry->format == EXIF_FORMAT_BYTE) {
			value = ptype_alloc("uint8");
			if (!value)
				return;
			PTYPE_UINT8_SETVAL(value, *entry->data);
		} else if (entry->format == EXIF_FORMAT_SHORT)	{
			value = ptype_alloc("uint16");
			if (!value)
				return;
			PTYPE_UINT16_SETVAL(value, exif_get_short(entry->data, byte_order));
		} else if (entry->format == EXIF_FORMAT_LONG) {
			value = ptype_alloc("uint32");
			if (!value)
				return;
			PTYPE_UINT32_SETVAL(value, exif_get_long(entry->data, byte_order));
		}

	}

	if (!value) {
		// Fallback for types not parsed by us yet
		// FIXME this is subject to the locale

		char buff[256];
		buff[sizeof(buff) - 1] = 0;
		exif_entry_get_value(entry, buff, sizeof(buff) - 1);

		value = ptype_alloc("string");
		if (!value)
			return;
		PTYPE_STRING_SETVAL(value, buff);

	}

	char *key = strdup(tag_name);
	if (!key) {
		pom_oom(strlen(tag_name) + 1);
		return;
	}

	struct data *data = analyzer_pload_buffer_get_data(pload);
	data_item_add_ptype(data, analyzer_jpeg_pload_exif, key, value);

}