Commit e02b29fd authored by Ghislain MARY's avatar Ghislain MARY

Remove old ICE implementation (as a filter).

parent 1a0b6bcc
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef ice_hh
#define ice_hh
#include <mediastreamer2/msfilter.h>
#include "ortp/stun_udp.h"
#include "ortp/stun.h"
#include "ortp/ortp.h"
/* list of state for STUN connectivity check */
#define ICE_PRUNED -1
#define ICE_FROZEN 0
#define ICE_WAITING 1
#define ICE_IN_PROGRESS 2 /* STUN request was sent */
#define ICE_SUCCEEDED 3
#define ICE_FAILED 4 /* no answer or unrecoverable failure */
struct SdpCandidate {
/* mandatory attributes: draft 19 */
int foundation;
int component_id;
char transport[20];
int priority;
char conn_addr[64];
int conn_port;
char cand_type[20];
char rel_addr[64];
int rel_port;
/* optionnal attributes: draft 19 */
char extension_attr[512]; /* *(SP extension-att-name SP extension-att-value) */
};
struct CandidatePair {
struct SdpCandidate local_candidate;
struct SdpCandidate remote_candidate;
long long pair_priority;
/* additionnal information */
int rem_controlling;
UInt96 tid;
int connectivity_check;
int retransmission_number;
uint64_t retransmission_time;
int nominated_pair;
};
#define MAX_ICE_CANDIDATES 10
struct IceCheckList {
struct CandidatePair cand_pairs[MAX_ICE_CANDIDATES];
int nominated_pair_index;
char loc_ice_ufrag[256];
char loc_ice_pwd[256];
char rem_ice_ufrag[256];
char rem_ice_pwd[256];
int rem_controlling;
uint64_t tiebreak_value;
#define ICE_CL_RUNNING 0
#define ICE_CL_COMPLETED 1
#define ICE_CL_FAILED 2
int state;
int RTO;
int Ta;
uint64_t keepalive_time;
};
#define MS_ICE_SET_SESSION MS_FILTER_METHOD(MS_ICE_ID,0,RtpSession*)
#define MS_ICE_SET_CANDIDATEPAIRS MS_FILTER_METHOD(MS_ICE_ID,1,struct CandidatePair*)
extern MSFilterDesc ms_ice_desc;
#endif
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#if !defined(WIN32) && !defined(_WIN32_WCE)
#ifdef __APPLE__
#include <sys/types.h>
#endif
#include <sys/socket.h>
#include <netdb.h>
#endif
#include "mediastreamer2/msticker.h"
#include "mediastreamer2/ice.h"
static void
ice_sendtest( struct IceCheckList *checklist, struct CandidatePair *remote_candidate, Socket myFd, StunAddress4 *dest,
const StunAtrString *username, const StunAtrString *password,
UInt96 *tid)
{
StunMessage req;
char buf[STUN_MAX_MESSAGE_SIZE];
int len = STUN_MAX_MESSAGE_SIZE;
memset(&req, 0, sizeof(StunMessage));
stunBuildReqSimple( &req, username, FALSE, FALSE, 1);
req.hasMessageIntegrity=TRUE;
/* 7.1.1.1
The attribute MUST be set equal to the priority that would be
assigned, based on the algorithm in Section 4.1.2, to a peer
reflexive candidate, should one be learned as a consequence of this
check */
req.hasPriority = TRUE;
req.priority.priority = (110 << 24) | (255 << 16) | (255 << 8)
| (256 - remote_candidate->remote_candidate.component_id);
/* TODO: put this parameter only for the candidate selected */
if (remote_candidate->nominated_pair==1)
req.hasUseCandidate = TRUE;
if (remote_candidate->rem_controlling==1)
{
req.hasIceControlled = TRUE;
req.iceControlled.value = checklist->tiebreak_value;
}
else
{
req.hasIceControlling = TRUE;
req.iceControlling.value = checklist->tiebreak_value;
}
/* TODO: not yet implemented? */
req.hasFingerprint = TRUE;
len = stunEncodeMessage( &req, buf, len, password );
memcpy(tid , &(req.msgHdr.tr_id), sizeof(req.msgHdr.tr_id));
sendMessage( myFd, buf, len, dest->addr, dest->port );
}
static int ice_restart(struct IceCheckList *checklist)
{
struct CandidatePair *remote_candidates = NULL;
int pos;
int count_waiting=0;
int count=0;
if (checklist==NULL)
return 0;
remote_candidates = checklist->cand_pairs;
if (remote_candidates==NULL)
return 0;
for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++)
{
if (strcasecmp(remote_candidates[pos].local_candidate.cand_type, "srflx")==0)
{
/* search for a highest priority "equivalent" pair */
int pos2;
for (pos2=0;pos2<pos && remote_candidates[pos2].remote_candidate.conn_addr[0]!='\0';pos2++)
{
/* same "base" address (origin of STUN connectivity check to the remote candidate */
if (strcasecmp(remote_candidates[pos].local_candidate.rel_addr, /* base address for "reflexive" address */
remote_candidates[pos2].local_candidate.conn_addr)==0) /* base address for "host" address */
{
/* if same target remote candidate: -> remove the one with lowest priority */
if (strcasecmp(remote_candidates[pos].remote_candidate.conn_addr,
remote_candidates[pos2].remote_candidate.conn_addr)==0)
{
/* useless cpair */
ms_message("ice.c: Removing useless pair (idx=%i)", pos);
remote_candidates[pos].connectivity_check = ICE_PRUNED;
}
}
}
}
}
/* no currently nominated pair */
checklist->nominated_pair_index = -1;
for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++)
{
if (remote_candidates[pos].connectivity_check == ICE_PRUNED)
continue;
if (remote_candidates[pos].connectivity_check == ICE_FROZEN)
remote_candidates[pos].connectivity_check = ICE_WAITING;
}
checklist->Ta = 40;
for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++)
{
if (remote_candidates[pos].connectivity_check == ICE_PRUNED)
continue;
if (remote_candidates[pos].connectivity_check == ICE_WAITING)
count_waiting++;
count++;
}
checklist->RTO = MAX(200, count*checklist->Ta*count_waiting);
return 0;
}
static int ice_sound_send_stun_request(RtpSession *session, struct IceCheckList *checklist, uint64_t ctime)
{
struct CandidatePair *remote_candidates = NULL;
if (checklist==NULL)
return 0;
remote_candidates = checklist->cand_pairs;
if (remote_candidates==NULL)
return 0;
{
struct CandidatePair *cand_pair;
int media_socket = rtp_session_get_rtp_socket(session);
StunAddress4 stunServerAddr;
StunAtrString username;
StunAtrString password;
bool_t res;
int pos;
/* prepare ONCE tie-break value */
if (checklist->tiebreak_value==0) {
checklist->tiebreak_value = random() * (0x7fffffffffffffffLL /0x7fff);
}
cand_pair=NULL;
for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++)
{
cand_pair = &remote_candidates[pos];
if (cand_pair->connectivity_check == ICE_PRUNED)
{
cand_pair=NULL;
continue;
}
if (cand_pair->connectivity_check == ICE_WAITING)
break;
if (cand_pair->connectivity_check == ICE_IN_PROGRESS)
break;
if (cand_pair->connectivity_check == ICE_SUCCEEDED)
break;
cand_pair=NULL;
}
if (cand_pair==NULL)
return 0; /* nothing to do: every pair is FAILED, FROZEN or PRUNED */
/* start first WAITING pair */
cand_pair=NULL;
for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++)
{
cand_pair = &remote_candidates[pos];
if (cand_pair->connectivity_check == ICE_PRUNED)
{
cand_pair=NULL;
continue;
}
if (cand_pair->connectivity_check == ICE_WAITING)
break;
cand_pair=NULL;
}
if (cand_pair!=NULL)
{
cand_pair->connectivity_check = ICE_IN_PROGRESS;
cand_pair->retransmission_number=0;
cand_pair->retransmission_time=ctime+checklist->RTO;
/* keep same rem_controlling for retransmission */
cand_pair->rem_controlling = checklist->rem_controlling;
}
/* try no nominate a pair if we are ready */
if (cand_pair==NULL && checklist->nominated_pair_index<0)
{
for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++)
{
cand_pair = &remote_candidates[pos];
if (cand_pair->connectivity_check == ICE_PRUNED)
{
cand_pair=NULL;
continue;
}
if (cand_pair->connectivity_check == ICE_SUCCEEDED)
{
break;
}
cand_pair=NULL;
}
/* ALWAYS accept "host" candidate that have succeeded */
if (cand_pair!=NULL
&& (strcasecmp(cand_pair->remote_candidate.cand_type, "host")==0))
{
checklist->nominated_pair_index = pos;
cand_pair->nominated_pair = 1;
cand_pair->connectivity_check = ICE_IN_PROGRESS;
cand_pair->retransmission_number=0;
cand_pair->retransmission_time=ctime+checklist->RTO;
/* keep same rem_controlling for retransmission */
cand_pair->rem_controlling = checklist->rem_controlling;
/* send a new STUN with USE-CANDIDATE */
ms_message("ice.c: nominating pair -> %i (%s:%i:%s -> %s:%i:%s) nominated=%s",
pos,
cand_pair->local_candidate.conn_addr,
cand_pair->local_candidate.conn_port,
cand_pair->local_candidate.cand_type,
cand_pair->remote_candidate.conn_addr,
cand_pair->remote_candidate.conn_port,
cand_pair->remote_candidate.cand_type,
cand_pair->nominated_pair==0?"FALSE":"TRUE");
checklist->keepalive_time=ctime+15*1000;
}
else if (cand_pair!=NULL)
{
struct CandidatePair *cand_pair2=NULL;
int pos2;
for (pos2=0;pos2<pos && remote_candidates[pos2].remote_candidate.conn_addr[0]!='\0';pos2++)
{
cand_pair2 = &remote_candidates[pos2];
if (cand_pair2->connectivity_check == ICE_PRUNED)
{
cand_pair2=NULL;
continue;
}
if (cand_pair2->connectivity_check == ICE_IN_PROGRESS
||cand_pair2->connectivity_check == ICE_WAITING)
{
break;
}
cand_pair2=NULL;
}
if (cand_pair2!=NULL)
{
/* a better candidate is still tested */
cand_pair=NULL;
}
else
{
checklist->nominated_pair_index = pos;
cand_pair->nominated_pair = 1;
cand_pair->connectivity_check = ICE_IN_PROGRESS;
cand_pair->retransmission_number=0;
cand_pair->retransmission_time=ctime+checklist->RTO;
/* keep same rem_controlling for retransmission */
cand_pair->rem_controlling = checklist->rem_controlling;
/* send a new STUN with USE-CANDIDATE */
ms_message("ice.c: nominating pair -> %i (%s:%i:%s -> %s:%i:%s) nominated=%s",
pos,
cand_pair->local_candidate.conn_addr,
cand_pair->local_candidate.conn_port,
cand_pair->local_candidate.cand_type,
cand_pair->remote_candidate.conn_addr,
cand_pair->remote_candidate.conn_port,
cand_pair->remote_candidate.cand_type,
cand_pair->nominated_pair==0?"FALSE":"TRUE");
checklist->keepalive_time=ctime+15*1000;
}
}
}
if (cand_pair==NULL)
{
/* no WAITING pair: retransmit after RTO */
for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++)
{
cand_pair = &remote_candidates[pos];
if (cand_pair->connectivity_check == ICE_PRUNED)
{
cand_pair=NULL;
continue;
}
if (cand_pair->connectivity_check == ICE_IN_PROGRESS
&& ctime > cand_pair->retransmission_time)
{
if (cand_pair->retransmission_number>7)
{
ms_message("ice.c: ICE_FAILED for candidate pair! %s:%i -> %s:%i",
cand_pair->local_candidate.conn_addr,
cand_pair->local_candidate.conn_port,
cand_pair->remote_candidate.conn_addr,
cand_pair->remote_candidate.conn_port);
cand_pair->connectivity_check = ICE_FAILED;
cand_pair=NULL;
continue;
}
cand_pair->retransmission_number++;
cand_pair->retransmission_time=ctime+checklist->RTO;
break;
}
cand_pair=NULL;
}
}
if (cand_pair==NULL)
{
if (checklist->nominated_pair_index<0)
return 0;
/* send STUN indication each 15 seconds: keepalive */
if (ctime>checklist->keepalive_time)
{
checklist->keepalive_time=ctime+15*1000;
for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++)
{
cand_pair = &remote_candidates[pos];
if (cand_pair->connectivity_check == ICE_SUCCEEDED)
{
res = stunParseServerName(cand_pair->remote_candidate.conn_addr,
&stunServerAddr);
if ( res == TRUE )
{
StunMessage req;
char buf[STUN_MAX_MESSAGE_SIZE];
int len = STUN_MAX_MESSAGE_SIZE;
stunServerAddr.port = cand_pair->remote_candidate.conn_port;
memset(&req, 0, sizeof(StunMessage));
stunBuildReqSimple( &req, NULL, FALSE, FALSE, 1);
req.msgHdr.msgType = (STUN_METHOD_BINDING|STUN_INDICATION);
req.hasFingerprint = TRUE;
len = stunEncodeMessage( &req, buf, len, NULL);
sendMessage( media_socket, buf, len, stunServerAddr.addr, stunServerAddr.port );
}
}
}
}
return 0;
}
username.sizeValue = 0;
password.sizeValue = 0;
/* username comes from "ice-ufrag" (rfrag:lfrag) */
/* ufrag and pwd are in first row only */
snprintf(username.value, sizeof(username.value), "%s:%s",
checklist->rem_ice_ufrag,
checklist->loc_ice_ufrag);
username.sizeValue = (uint16_t)strlen(username.value);
snprintf(password.value, sizeof(password.value), "%s",
checklist->rem_ice_pwd);
password.sizeValue = (uint16_t)strlen(password.value);
res = stunParseServerName(cand_pair->remote_candidate.conn_addr,
&stunServerAddr);
if ( res == TRUE )
{
ms_message("ice.c: STUN REQ (%s) -> %i (%s:%i:%s -> %s:%i:%s) nominated=%s",
cand_pair->nominated_pair==0?"":"USE-CANDIDATE",
pos,
cand_pair->local_candidate.conn_addr,
cand_pair->local_candidate.conn_port,
cand_pair->local_candidate.cand_type,
cand_pair->remote_candidate.conn_addr,
cand_pair->remote_candidate.conn_port,
cand_pair->remote_candidate.cand_type,
cand_pair->nominated_pair==0?"FALSE":"TRUE");
stunServerAddr.port = cand_pair->remote_candidate.conn_port;
ice_sendtest(checklist, cand_pair, media_socket, &stunServerAddr, &username, &password,
&(cand_pair->tid));
}
}
return 0;
}
#if 0
static int
_ice_get_localip_for (struct sockaddr_storage *saddr, size_t saddr_len, char *loc, int size)
{
int err, tmp;
int sock;
struct sockaddr_storage addr;
socklen_t addr_len;
strcpy (loc, "127.0.0.1"); /* always fallback to local loopback */
sock = socket (saddr->ss_family, SOCK_DGRAM, 0);
tmp = 1;
err = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &tmp, sizeof (int));
if (err < 0)
{
ms_error("ice.c: Error in setsockopt");
closesocket (sock);
return -1;
}
err = connect (sock, (struct sockaddr*)saddr, saddr_len);
if (err < 0)
{
ms_error("ice.c: Error in connect");
closesocket (sock);
return -1;
}
addr_len = sizeof (addr);
err = getsockname (sock, (struct sockaddr *) &addr, (socklen_t*)&addr_len);
if (err != 0)
{
ms_error("ice.c: Error in getsockname");
closesocket (sock);
return -1;
}
err = getnameinfo ((struct sockaddr *) &addr, addr_len, loc, size, NULL, 0, NI_NUMERICHOST);
if (err != 0)
{
ms_error("ice.c: Error in getnameinfo");
closesocket (sock);
return -1;
}
closesocket (sock);
/* ms_message("ice.c: Outgoing interface for sending STUN answer is %s", loc); */
return 0;
}
#endif
static void
_ice_createErrorResponse(StunMessage *response, int cl, int number, const char* msg)
{
response->msgHdr.msgType = (STUN_METHOD_BINDING | STUN_ERR_RESP);
response->hasErrorCode = TRUE;
response->errorCode.errorClass = cl;
response->errorCode.number = number;
strcpy(response->errorCode.reason, msg);
response->errorCode.sizeReason = strlen(msg);
response->hasFingerprint = TRUE;
}
static int ice_process_stun_message(RtpSession *session, struct IceCheckList *checklist, OrtpEvent *evt)
{
struct CandidatePair *remote_candidates = NULL;
StunMessage msg;
bool_t res;
//int highest_priority_success=-1;
OrtpEventData *evt_data = ortp_event_get_data(evt);
mblk_t *mp = evt_data->packet;
struct sockaddr_in *udp_remote;
char src6host[NI_MAXHOST];
int recvport = 0;
int i;
udp_remote = (struct sockaddr_in*)&evt_data->ep->addr;
memset( &msg, 0 , sizeof(msg) );
res = stunParseMessage((char*)mp->b_rptr, mp->b_wptr-mp->b_rptr, &msg);
if (!res)
{
ms_error("ice.c: Malformed STUN packet.");
return -1;
}
if (checklist==NULL)
{
ms_error("ice.c: dropping STUN packet: ice is not configured");
return -1;
}
remote_candidates = checklist->cand_pairs;
if (remote_candidates==NULL)
{
ms_error("ice.c: dropping STUN packet: ice is not configured");
return -1;
}
/* prepare ONCE tie-break value */
if (checklist->tiebreak_value==0) {
checklist->tiebreak_value = random() * (0x7fffffffffffffffLL/0x7fff);
}
memset (src6host, 0, sizeof (src6host));
{
struct sockaddr_storage *aaddr = (struct sockaddr_storage *)&evt_data->ep->addr;
if (aaddr->ss_family==AF_INET)
recvport = ntohs (((struct sockaddr_in *) udp_remote)->sin_port);
else
recvport = ntohs (((struct sockaddr_in6 *) &evt_data->ep->addr)->sin6_port);
}
i = getnameinfo ((struct sockaddr*)&evt_data->ep->addr, evt_data->ep->addrlen,
src6host, NI_MAXHOST,
NULL, 0, NI_NUMERICHOST);
if (i != 0)
{
ms_error("ice.c: Error with getnameinfo");
return -1;
}
if (STUN_IS_REQUEST(msg.msgHdr.msgType))
ms_message("ice.c: STUN_CONNECTIVITYCHECK: Request received from: %s:%i",
src6host, recvport);
else if (STUN_IS_INDICATION(msg.msgHdr.msgType))
ms_message("ice.c: SUN_INDICATION: Request Indication received from: %s:%i",
src6host, recvport);
else
ms_message("ice.c: STUN_ANSWER: Answer received from: %s:%i",
src6host, recvport);
{
int pos;
for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++)
{
struct CandidatePair *cand_pair = &remote_candidates[pos];
if (cand_pair->connectivity_check == ICE_SUCCEEDED)
{
//highest_priority_success=pos;
break;
}
}
}
if (STUN_IS_INDICATION(msg.msgHdr.msgType))
{
ms_message("ice.c: STUN INDICATION <- (?:?:? <- %s:%i:?)", src6host, recvport);
return 0;
}
else if (STUN_IS_REQUEST(msg.msgHdr.msgType))
{
StunMessage resp;
StunAtrString hmacPassword;
StunAddress4 remote_addr;
int rtp_socket;
memset( &resp, 0 , sizeof(resp));
remote_addr.addr = ntohl(udp_remote->sin_addr.s_addr);
remote_addr.port = ntohs(udp_remote->sin_port);
rtp_socket = rtp_session_get_rtp_socket(session);
resp.msgHdr.magic_cookie = ntohl(msg.msgHdr.magic_cookie);
for (i=0; i<12; i++ )
{
resp.msgHdr.tr_id.octet[i] = msg.msgHdr.tr_id.octet[i];
}
/* check mandatory params */
if (!msg.hasUsername)
{
char buf[STUN_MAX_MESSAGE_SIZE];
int len = sizeof(buf);
ms_error("ice.c: STUN REQ <- Missing USERNAME attribute in connectivity check");
_ice_createErrorResponse(&resp, 4, 32, "Missing USERNAME attribute");
len = stunEncodeMessage(&resp, buf, len, &hmacPassword );
if (len)
sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port);
return -1;
}
if (!msg.hasMessageIntegrity)
{
char buf[STUN_MAX_MESSAGE_SIZE];
int len = sizeof(buf);
ms_error("ice.c: STUN REQ <- Missing MESSAGEINTEGRITY attribute in connectivity check");
_ice_createErrorResponse(&resp, 4, 1, "Missing MESSAGEINTEGRITY attribute");
len = stunEncodeMessage(&resp, buf, len, &hmacPassword );
if (len)
sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port);
return -1;
}
/*
The password associated with that transport address ID is used to verify
the MESSAGE-INTEGRITY attribute, if one was present in the request.
*/
{
char hmac[20];
/* remove length of fingerprint if present */
if (msg.hasFingerprint==TRUE)
{
char *lenpos = (char *)mp->b_rptr + sizeof(uint16_t);
uint16_t newlen = htons(msg.msgHdr.msgLength-8); /* remove fingerprint size */
memcpy(lenpos, &newlen, sizeof(uint16_t));
stunCalculateIntegrity_shortterm(hmac, (char*)mp->b_rptr, mp->b_wptr-mp->b_rptr-24-8, checklist->loc_ice_pwd);
}
else
stunCalculateIntegrity_shortterm(hmac, (char*)mp->b_rptr, mp->b_wptr-mp->b_rptr-24, checklist->loc_ice_pwd);
if (memcmp(msg.messageIntegrity.hash, hmac, 20)!=0)
{
char buf[STUN_MAX_MESSAGE_SIZE];
int len = sizeof(buf);
ms_error("ice.c: STUN REQ <- Wrong MESSAGEINTEGRITY attribute in connectivity check");
_ice_createErrorResponse(&resp, 4, 1, "Wrong MESSAGEINTEGRITY attribute");
len = stunEncodeMessage(&resp, buf, len, &hmacPassword );
if (len)
sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port);
return -1;
}
if (msg.hasFingerprint==TRUE)
{
char *lenpos = (char *)mp->b_rptr + sizeof(uint16_t);
uint16_t newlen = htons(msg.msgHdr.msgLength); /* add back fingerprint size */
memcpy(lenpos, &newlen, sizeof(uint16_t));
}
}
/* 7.2.1.1. Detecting and Repairing Role Conflicts */