mirror of
				https://git.haproxy.org/git/haproxy.git/
				synced 2025-10-31 00:21:00 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			265 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			265 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Financial Information eXchange Protocol
 | |
|  *
 | |
|  * Copyright 2020 Baptiste Assmann <bedis9@gmail.com>
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU General Public License
 | |
|  * as published by the Free Software Foundation; either version
 | |
|  * 2 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <haproxy/intops.h>
 | |
| #include <haproxy/fix.h>
 | |
| /*
 | |
|  * Return the corresponding numerical tag id if <str> looks like a valid FIX
 | |
|  * protocol tag ID. Otherwise, 0 is returned (0 is an invalid id).
 | |
|  *
 | |
|  * If <version> is given, it must be one of a defined FIX version string (see
 | |
|  * FIX_X_Y macros). In this case, the function will also check tag ID ranges. If
 | |
|  * no <version> is provided, any strictly positive integer is valid.
 | |
|  *
 | |
|  * tag ID range depends on FIX protocol version:
 | |
|  *    - FIX.4.0:    1-140
 | |
|  *    - FIX.4.1:    1-211
 | |
|  *    - FIX.4.2:    1-446
 | |
|  *    - FIX.4.3:    1-659
 | |
|  *    - FIX.4.4:    1-956
 | |
|  *    - FIX.5.0:    1-1139
 | |
|  *    - FIX.5.0SP1: 1-1426
 | |
|  *    - FIX.5.0SP2: 1-1621
 | |
|  * range 10000 to 19999 is for "user defined tags"
 | |
|  */
 | |
| unsigned int fix_check_id(const struct ist str, const struct ist version) {
 | |
| 	const char *s, *end;
 | |
| 	unsigned int ret;
 | |
| 
 | |
| 	s = istptr(str);
 | |
| 	end = istend(str);
 | |
| 	ret = read_uint(&s, end);
 | |
| 
 | |
| 	/* we did not consume all characters from <str>, this is an error */
 | |
| 	if (s != end)
 | |
| 		return 0;
 | |
| 
 | |
| 	/* field ID can't be 0 */
 | |
| 	if (ret == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	/* we can leave now if version was not provided */
 | |
| 	if (!isttest(version))
 | |
| 		return ret;
 | |
| 
 | |
| 	/* we can leave now if this is a "user defined tag id" */
 | |
| 	if (ret >= 10000 && ret <= 19999)
 | |
| 		return ret;
 | |
| 
 | |
| 	/* now perform checking per FIX version */
 | |
| 	if (istissame(FIX_4_0, version) && (ret <= 140))
 | |
| 		return ret;
 | |
| 	else if (istissame(FIX_4_1, version) && (ret <= 211))
 | |
| 		return ret;
 | |
| 	else if (istissame(FIX_4_2, version) && (ret <= 446))
 | |
| 		return ret;
 | |
| 	else if (istissame(FIX_4_3, version) && (ret <= 659))
 | |
| 		return ret;
 | |
| 	else if (istissame(FIX_4_4, version) && (ret <= 956))
 | |
| 		return ret;
 | |
| 	/* version string is the same for all 5.0 versions, so we can only take
 | |
| 	 * into consideration the biggest range
 | |
| 	 */
 | |
| 	else if (istissame(FIX_5_0, version) && (ret <= 1621))
 | |
| 		return ret;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Parse a FIX message <msg> and performs following sanity checks:
 | |
|  *
 | |
|  *   - checks tag ids and values are not empty
 | |
|  *   - checks tag ids are numerical value
 | |
|  *   - checks the first tag is BeginString with a valid version
 | |
|  *   - checks the second tag is BodyLength with the right body length
 | |
|  *   - checks the third tag is MsgType
 | |
|  *   - checks the last tag is CheckSum with a valid checksum
 | |
|  *
 | |
|  * Returns:
 | |
|  *  FIX_INVALID_MESSAGE if the message is invalid
 | |
|  *  FIX_NEED_MORE_DATA  if we need more data to fully validate the message
 | |
|  *  FIX_VALID_MESSAGE   if the message looks valid
 | |
|  */
 | |
| int fix_validate_message(const struct ist msg)
 | |
| {
 | |
| 	struct ist parser, version;
 | |
| 	unsigned int tagnum, bodylen;
 | |
| 	unsigned char checksum;
 | |
| 	char *body;
 | |
| 	int ret = FIX_INVALID_MESSAGE;
 | |
| 
 | |
| 	if (istlen(msg) < FIX_MSG_MINSIZE) {
 | |
| 		ret = FIX_NEED_MORE_DATA;
 | |
| 		goto end;
 | |
| 	}
 | |
| 
 | |
| 	/* parsing the whole message to compute the checksum and check all tag
 | |
| 	 * ids are properly set. Here we are sure to have the 2 first tags. Thus
 | |
| 	 * the version and the body length can be checked.
 | |
| 	 */
 | |
| 	parser = msg;
 | |
| 	version = IST_NULL;
 | |
| 	checksum = tagnum = bodylen = 0;
 | |
| 	body = NULL;
 | |
| 	while (istlen(parser) > 0) {
 | |
| 		struct ist tag, value;
 | |
| 		unsigned int tagid;
 | |
| 		const char *p, *end;
 | |
| 
 | |
| 		/* parse the tag ID and its value and perform first sanity checks */
 | |
| 		value = iststop(istfind(parser, '='), FIX_DELIMITER);
 | |
| 
 | |
| 		/* end of value not found */
 | |
| 		if (istend(value) == istend(parser)) {
 | |
| 			ret = FIX_NEED_MORE_DATA;
 | |
| 			goto end;
 | |
| 		}
 | |
| 		/* empty tag or empty value are forbidden */
 | |
| 		if (istptr(parser) == istptr(value) ||!istlen(value))
 | |
| 			goto end;
 | |
| 
 | |
| 		/* value points on '='. get the tag and skip '=' */
 | |
| 		tag = ist2(istptr(parser), istptr(value) - istptr(parser));
 | |
| 		value = istnext(value);
 | |
| 
 | |
| 		/* Check the tag id */
 | |
| 		tagid = fix_check_id(tag, version);
 | |
| 		if (!tagid)
 | |
| 			goto end;
 | |
| 		tagnum++;
 | |
| 
 | |
| 		if (tagnum == 1) {
 | |
| 			/* the first tag must be BeginString */
 | |
| 			if (tagid != FIX_TAG_BeginString)
 | |
| 				goto end;
 | |
| 
 | |
| 			version = fix_version(value);
 | |
| 			if (!isttest(version))
 | |
| 				goto end;
 | |
| 		}
 | |
| 		else if (tagnum == 2) {
 | |
| 			/* the second tag must be bodyLength */
 | |
| 			if (tagid != FIX_TAG_BodyLength)
 | |
| 				goto end;
 | |
| 
 | |
| 			p = istptr(value);
 | |
| 			end = istend(value);
 | |
| 			bodylen = read_uint(&p, end);
 | |
| 
 | |
| 			/* we did not consume all characters from <str> or no body, this is an error.
 | |
| 			 * There is at least the message type in the body.
 | |
| 			 */
 | |
| 			if (p != end || !bodylen)
 | |
| 				goto end;
 | |
| 
 | |
| 			body = istend(value) + 1;
 | |
| 		}
 | |
| 		else if (tagnum == 3) {
 | |
| 			/* the third tag must be MsgType */
 | |
| 			if (tagid != FIX_TAG_MsgType)
 | |
| 				goto end;
 | |
| 		}
 | |
| 		else if (tagnum > 3 && tagid == FIX_TAG_CheckSum) {
 | |
| 			/* CheckSum tag should be the last one and is not taken into account
 | |
| 			 * to compute the checksum itself and the body length. The value is
 | |
| 			 * a three-octet representation of the checksum decimal value.
 | |
| 			 */
 | |
| 			if (bodylen != istptr(parser) - body)
 | |
| 				goto end;
 | |
| 
 | |
| 			if (istlen(value) != 3)
 | |
| 				goto end;
 | |
| 			if (checksum != strl2ui(istptr(value), istlen(value)))
 | |
| 				goto end;
 | |
| 
 | |
| 			/* End of the message, exit from the loop */
 | |
| 			ret = FIX_VALID_MESSAGE;
 | |
| 			goto end;
 | |
| 		}
 | |
| 
 | |
| 		/* compute checksum of tag=value<delim> */
 | |
| 		for (p = istptr(tag) ; p < istend(tag) ; ++p)
 | |
| 			checksum += *p;
 | |
| 		checksum += '=';
 | |
| 		for (p = istptr(value) ; p < istend(value) ; ++p)
 | |
| 			checksum += *p;
 | |
| 		checksum += FIX_DELIMITER;
 | |
| 
 | |
| 		/* move the parser after the value and its delimiter */
 | |
| 		parser = istadv(parser, istlen(tag) + istlen(value) + 2);
 | |
| 	}
 | |
| 
 | |
| 	if (body) {
 | |
| 		/* We start to read the body but we don't reached the checksum tag */
 | |
| 		ret = FIX_NEED_MORE_DATA;
 | |
| 	}
 | |
| 
 | |
|   end:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Iter on a FIX message <msg> and return the value of <tagid>.
 | |
|  *
 | |
|  * Returns the corresponding value if <tagid> is found. If <tagid> is not found
 | |
|  * because more data are required, the message with a length set to 0 is
 | |
|  * returned. If <tagid> is not found in the message or if the message is
 | |
|  * invalid, IST_NULL is returned.
 | |
|  *
 | |
|  * Note: Only simple sanity checks are performed on tags and values (not empty).
 | |
|  *
 | |
|  * the tag looks like
 | |
|  *   <tagid>=<value>FIX_DELIMITER with <tag> and <value> not empty
 | |
|  */
 | |
| struct ist fix_tag_value(const struct ist msg, unsigned int tagid)
 | |
| {
 | |
| 	struct ist parser, t, v;
 | |
| 	unsigned int id;
 | |
| 
 | |
| 	parser = msg;
 | |
| 	while (istlen(parser) > 0) {
 | |
| 		v  = iststop(istfind(parser, '='), FIX_DELIMITER);
 | |
| 
 | |
| 		/* delimiter not found, need more data */
 | |
| 		if (istend(v) == istend(parser))
 | |
| 			break;
 | |
| 
 | |
| 		/* empty tag or empty value, invalid */
 | |
| 		if (istptr(parser) == istptr(v) || !istlen(v))
 | |
| 			goto not_found_or_invalid;
 | |
| 
 | |
| 		t = ist2(istptr(parser), istptr(v) - istptr(parser));
 | |
| 		v = istnext(v);
 | |
| 
 | |
| 		id = fix_check_id(t, IST_NULL);
 | |
| 		if (!id)
 | |
| 			goto not_found_or_invalid;
 | |
| 		if (id == tagid) {
 | |
| 			/* <tagId> found, return the corresponding value */
 | |
| 			return v;
 | |
| 		}
 | |
| 
 | |
| 		/* CheckSum tag is the last one, no <tagid> found */
 | |
| 		if (id == FIX_TAG_CheckSum)
 | |
| 			goto not_found_or_invalid;
 | |
| 
 | |
| 		parser = istadv(parser, istlen(t) + istlen(v) + 2);
 | |
| 	}
 | |
| 	/* not enough data to find <tagid> */
 | |
| 	return ist2(istptr(msg), 0);
 | |
| 
 | |
|   not_found_or_invalid:
 | |
| 	return IST_NULL;
 | |
| }
 |