/* Does a pile of bits on the floor look like a 32 bit service_request_pkt
	when squinted at in the right light? */
bool sreq_is_32bit(char *pkt)
{
	uint32_t ticket;
	uint16_t service;

	/* first we sanity check the ticket and see if the ticket field is the
		correct authentication number */
	ticket = unpack_uint32_t(pkt, SREQ32_ticket);
	ticket = network_uint32_t_order_to_host_uint32_t_order(ticket);
	if (ticket != AUTHENTICATION_TCKT) {
		return false;
	}
	
	/* To check the validity of this packet, I extract the second field
		out of the packet, the "service" field. With a 64 bit packet, this
		should be zero (at the offset of the field in a 32 bit packet) because
		this is the high 32 bits of the 64 bit "ticket" field, which comes
		before it in memory. On a 32 bit machine, the service type should be
		within the set defined by the service_type enum. 
	*/

	/* ok, extract the service type, and see if it is in the right set */
	service = unpack_uint16_t(pkt, SREQ32_service);
	service = network_uint16_t_order_to_host_uint16_t_order(service);

	if (service < CKPT_SERVER_SERVICE_STATUS || 
		service > SERVICE_ABORT_REPLICATION)
	{
		/* oops, can't be a 32 bit packet */
		return false;
	}
	
	/* I guess it passed! Heuristics to the rescue! */
	return true;
}
/* Does a pile of bits on the floor look like a 64 bit service_request_pkt
	when squinted at in the right light? */
bool sreq_is_64bit(char *pkt)
{
	uint64_t ticket;
	uint16_t service;

	/* first we sanity check the ticket and see if the ticket field is the
		correct authentication number */
	ticket = unpack_uint64_t(pkt, SREQ64_ticket);
	ticket = network_uint64_t_order_to_host_uint64_t_order(ticket);

	if (ticket != AUTHENTICATION_TCKT) {
		return false;
	}
	
	/* To check the validity of this packet, I extract the second field
		out of the packet, the "service" field. With a 64 bit packet, this
		should be in range (at the offset of the 64 bit packet). If
		not, then I guess it isn't a 64 bit packet. 
	*/

	/* ok, extract the service type, and see if it is in the right set */
	service = unpack_uint16_t(pkt, SREQ64_service);
	service = network_uint16_t_order_to_host_uint16_t_order(service);

	if (service < CKPT_SERVER_SERVICE_STATUS || 
		service > SERVICE_ABORT_REPLICATION)
	{
		/* oops, can't be a 64 bit packet */
		return false;
	}
	
	/* I guess it passed! Heuristics to the rescue! */
	return true;
}
msg_offset unpack_vehicle_termination_command(
   msg_offset offset,
   vehicle_termination_command_t* out_ptr)
{
   offset = unpack_float64_t(offset, &out_ptr->timestamp);
   offset = unpack_uint16_t(offset, &out_ptr->vehicle_ID);
   offset = unpack_uint8_t(offset, &out_ptr->termination_mode);
   return offset;
}
msg_offset unpack_vehicle_authorization_reply(
   msg_offset offset,
   vehicle_authorization_reply_t* out_ptr)
{
   offset = unpack_float64_t(offset, &out_ptr->timestamp);
   offset = unpack_uint16_t(offset, &out_ptr->vehicle_ID);
   offset = unpack_uint8_t(offset, &out_ptr->vehicle_type);
   offset = unpack_uint8_t(offset, &out_ptr->authorized_services);
   offset = unpack_uint8_t(offset, &out_ptr->granted_services);
   return offset;
}
/* This function accepts the *first* packet on a ready service req socket.
	Its job is to figure out if the client side is 32 bits or 64 bits, 
	read the appropriate information off the socket, and set up the
	FDContext with the fd and the knowledge of what type of bit width
	the client side is. This function also translates between the host
	service_req_socket and whatever the client has.
*/
int recv_service_req_pkt(service_req_pkt *srq, FDContext *fdc)
{
	size_t bytes_recvd;
	char netpkt[SREQ_PKTSIZE_MAX];
	read_result_t ret;
	int ok;
	size_t diff;

	/* We better not know what the client bit width is when this function is
		called, since it figures it out! */
	ASSERT(fdc->type == FDC_UNKNOWN);

	memset(netpkt, 0, SREQ_PKTSIZE_MAX);

	/* Read the *smallest* of the two possible structure
		widths, this will ensure we don't deadlock reading more
		bytes that will never come.
	*/
	ret = net_read_with_timeout(fdc->fd, netpkt, SREQ_PKTSIZE_MIN, &bytes_recvd,
								REQUEST_TIMEOUT);
	switch(ret) {
		case NET_READ_FAIL:
			Server::Log("Failed to read initial service_req_pkt "
						"packet length!");
			return PC_NOT_OK;
			break;

		case NET_READ_TIMEOUT:
			Server::Log("Timed out while reading initial service_req_pkt "
						"packet length!");
			return PC_NOT_OK;
			break;

		case NET_READ_OK:
			/* ensure I read how much I meant to read */
			if (bytes_recvd != SREQ_PKTSIZE_MIN) {
				Server::Log("Short read while reading a possible 32-bit "
							"service_req_pkt.");
				return PC_NOT_OK;
			}

			/* all good, keep the control flow going */

			break;

		default:
			/* Normally, one would except, but why take down a whole server
				when we could just close this errant connection? */
			Server::Log("Programmer error: unhandled return code on "
						"net_read_with_timeout() with suspected 32 bit client "
						"while handling a service_req_pkt");
			return PC_NOT_OK;
			break;
	}

	/* Figure out what kind of packet it is */
	ok = sreq_is_32bit(netpkt);
	if (!ok) {
		/* try to read the rest of the pkt, assuming it is a 64 bit packet */
		diff = SREQ_PKTSIZE_64 - SREQ_PKTSIZE_32;

		ret = net_read_with_timeout(fdc->fd, netpkt + SREQ_PKTSIZE_32,
									diff, &bytes_recvd, REQUEST_TIMEOUT);
		switch(ret) {
			case NET_READ_FAIL:
				Server::Log("Failed to read 64 bit portion of pkt");
				return PC_NOT_OK;
				break;

			case NET_READ_TIMEOUT:
				Server::Log("Timed out while reading 64 bit portion of pkt");
				return PC_NOT_OK;
				break;

			case NET_READ_OK:
				/* ensure I read how much I meant to read */
				if (bytes_recvd != diff) {
					Server::Log("Short read while reading the remainder of "
								"a possible 64-bit service_req_pkt.");
					return PC_NOT_OK;
				}

				/* all good, keep the control flow going */

				break;

			default:
				/* Normally, one would except, but why take down a whole server
					when we could just close this errant connection? */
				Server::Log("Programmer error: unhandled return code on "
							"net_read_with_timeout() with suspected 64 "
							"bit client while handling a service_req_pkt");
				return PC_NOT_OK;
				break;
		}

		/* now, see if we can find it in the 64 bit version of the packet */
		ok = sreq_is_64bit(netpkt);
		if (!ok) {
			/* oops, we didn't find the hard coded ticket in either context
				of 32 bit or 64 bit, so apparently, we didn't read the
				expected kind of packet on the wire or it was garbage.
				fdc stays unknown.
			*/
			Server::Log("Could not determine if packet is a service_req_pkt! "
						"Aborting connection!");
			return PC_NOT_OK;
		}

		/* unpack the 64 bit case into the host structure. Don't forget
			to undo the network byte ordering. */

		srq->ticket = unpack_uint64_t(netpkt, SREQ64_ticket);
		srq->ticket =
			network_uint64_t_order_to_host_uint64_t_order(srq->ticket);

		srq->service = unpack_uint16_t(netpkt, SREQ64_service);
		srq->service =
			network_uint16_t_order_to_host_uint16_t_order(srq->service);

		srq->key = unpack_uint64_t(netpkt, SREQ64_key);
		srq->key =
			network_uint64_t_order_to_host_uint64_t_order(srq->key);

		memmove(srq->owner_name,
			unpack_char_array(netpkt, SREQ64_owner_name),
			MAX_NAME_LENGTH);

		memmove(srq->file_name,
			unpack_char_array(netpkt, SREQ64_file_name),
			MAX_CONDOR_FILENAME_LENGTH);

		memmove(srq->new_file_name,
			unpack_char_array(netpkt, SREQ64_new_file_name),
			MAX_CONDOR_FILENAME_LENGTH-4);

		srq->shadow_IP = unpack_in_addr(netpkt, SREQ64_shadow_IP);
		srq->shadow_IP.s_addr =
			network_uint32_t_order_to_host_uint32_t_order(srq->shadow_IP.s_addr);

		Server::Log("Client is using the 64 bit protocol.");

		fdc->type = FDC_64;
		return PC_OK;
	}

	/* unpack the 32 bit case into the host structure. Don't forget
		to undo the network byte ordering. */
	srq->ticket = unpack_uint32_t(netpkt, SREQ32_ticket);
	srq->ticket = network_uint32_t_order_to_host_uint32_t_order(srq->ticket);

	srq->service = unpack_uint16_t(netpkt, SREQ32_service);
	srq->service = network_uint16_t_order_to_host_uint16_t_order(srq->service);

	srq->key = unpack_uint32_t(netpkt, SREQ32_key);
	srq->key = network_uint32_t_order_to_host_uint32_t_order(srq->key);

	memmove(srq->owner_name,
		unpack_char_array(netpkt, SREQ32_owner_name),
		MAX_NAME_LENGTH);

	memmove(srq->file_name,
		unpack_char_array(netpkt, SREQ32_file_name),
		MAX_CONDOR_FILENAME_LENGTH);

	memmove(srq->new_file_name,
		unpack_char_array(netpkt, SREQ32_new_file_name),
		MAX_CONDOR_FILENAME_LENGTH-4);

	srq->shadow_IP = unpack_in_addr(netpkt, SREQ32_shadow_IP);
	srq->shadow_IP.s_addr =
		network_uint32_t_order_to_host_uint32_t_order(srq->shadow_IP.s_addr);

	Server::Log("Client is using the 32 bit protocol.");

	fdc->type = FDC_32;
	return PC_OK;
}