diff options
Diffstat (limited to 'programs/pluto/modecfg.c')
-rw-r--r-- | programs/pluto/modecfg.c | 798 |
1 files changed, 798 insertions, 0 deletions
diff --git a/programs/pluto/modecfg.c b/programs/pluto/modecfg.c new file mode 100644 index 000000000..1c22845a5 --- /dev/null +++ b/programs/pluto/modecfg.c @@ -0,0 +1,798 @@ +/* Mode config related functions + * Copyright (C) 2001-2002 Colubris Networks + * Copyright (C) 2003 Sean Mathews - Nu Tech Software Solutions, inc. + * Copyright (C) 2003-2004 Xelerance Corporation + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * RCSID $Id: modecfg.c,v 1.6 2006/04/24 20:44:57 as Exp $ + * + * This code originally written by Colubris Networks, Inc. + * Extraction of patch and porting to 1.99 codebases by Xelerance Corporation + * Porting to 2.x by Sean Mathews + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <freeswan.h> + +#include "constants.h" +#include "defs.h" +#include "state.h" +#include "demux.h" +#include "timer.h" +#include "ipsec_doi.h" +#include "log.h" +#include "md5.h" +#include "sha1.h" +#include "crypto.h" +#include "modecfg.h" +#include "whack.h" + +/* + * Addresses assigned (usually via MODE_CONFIG) to the Initiator + */ +struct internal_addr +{ + ip_address ipaddr; + ip_address dns[2]; + ip_address wins[2]; +}; + +/* + * Get inside IP address for a connection + */ +static void +get_internal_addresses(struct connection *c, struct internal_addr *ia) +{ + zero(ia); + + if (isanyaddr(&c->spd.that.host_srcip)) + { + /* not defined in connection - fetch it from LDAP */ + } + else + { + ia->ipaddr = c->spd.that.host_srcip; + } +} + +/* + * Compute HASH of Mode Config. + */ +static size_t +mode_cfg_hash(u_char *dest, const u_char *start, const u_char *roof + , const struct state *st) +{ + struct hmac_ctx ctx; + + hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid_a); + hmac_update(&ctx, (const u_char *) &st->st_msgid, sizeof(st->st_msgid)); + hmac_update(&ctx, start, roof-start); + hmac_final(dest, &ctx); + + DBG(DBG_CRYPT, + DBG_log("MODE CFG: HASH computed:"); + DBG_dump("", dest, ctx.hmac_digest_size) + ) + return ctx.hmac_digest_size; +} + + +/* Mode Config Reply + * Generates a reply stream containing Mode Config information (eg: IP, DNS, WINS) + */ +stf_status modecfg_resp(struct state *st + , u_int resp + , pb_stream *rbody + , u_int16_t replytype + , bool hackthat + , u_int16_t ap_id) +{ + u_char *r_hash_start,*r_hashval; + + /* START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_ATTR); */ + + { + pb_stream hash_pbs; + int np = ISAKMP_NEXT_ATTR; + + if (!out_generic(np, &isakmp_hash_desc, rbody, &hash_pbs)) + return STF_INTERNAL_ERROR; + r_hashval = hash_pbs.cur; /* remember where to plant value */ + if (!out_zero(st->st_oakley.hasher->hash_digest_size, &hash_pbs, "HASH")) + return STF_INTERNAL_ERROR; + close_output_pbs(&hash_pbs); + r_hash_start = (rbody)->cur; /* hash from after HASH payload */ + } + + /* ATTR out */ + { + struct isakmp_mode_attr attrh; + struct isakmp_attribute attr; + pb_stream strattr,attrval; + int attr_type; + struct internal_addr ia; + int dns_idx, wins_idx; + bool dont_advance; + + attrh.isama_np = ISAKMP_NEXT_NONE; + attrh.isama_type = replytype; + + attrh.isama_identifier = ap_id; + if (!out_struct(&attrh, &isakmp_attr_desc, rbody, &strattr)) + return STF_INTERNAL_ERROR; + + get_internal_addresses(st->st_connection, &ia); + + if (!isanyaddr(&ia.dns[0])) /* We got DNS addresses, answer with those */ + resp |= LELEM(INTERNAL_IP4_DNS); + else + resp &= ~LELEM(INTERNAL_IP4_DNS); + + if (!isanyaddr(&ia.wins[0])) /* We got WINS addresses, answer with those */ + resp |= LELEM(INTERNAL_IP4_NBNS); + else + resp &= ~LELEM(INTERNAL_IP4_NBNS); + + if (hackthat) + { + if (memcmp(&st->st_connection->spd.that.client.addr + ,&ia.ipaddr + ,sizeof(ia.ipaddr)) != 0) + { + /* Make the Internal IP address and Netmask + * as that client address + */ + st->st_connection->spd.that.client.addr = ia.ipaddr; + st->st_connection->spd.that.client.maskbits = 32; + st->st_connection->spd.that.has_client = TRUE; + } + } + + attr_type = 0; + dns_idx = 0; + wins_idx = 0; + + while (resp != 0) + { + dont_advance = FALSE; + if (resp & 1) + { + const u_char *byte_ptr; + u_int len; + + /* ISAKMP attr out */ + attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TLV; + out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, &attrval); + + switch (attr_type) + { + case INTERNAL_IP4_ADDRESS: + { + char srcip[ADDRTOT_BUF]; + + addrtot(&ia.ipaddr, 0, srcip, sizeof(srcip)); + plog("assigning virtual IP source address %s", srcip); + len = addrbytesptr(&ia.ipaddr, &byte_ptr); + out_raw(byte_ptr,len,&attrval,"IP4_addr"); + } + break; + case INTERNAL_IP4_NETMASK: + { + u_int mask; +#if 0 + char mask[4],bits[8]={0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe}; + int t,m=st->st_connection->that.host_addr.maskbit; + for (t=0; t<4; t++) + { + if (m < 8) + mask[t] = bits[m]; + else + mask[t] = 0xff; + m -= 8; + } +#endif + if (st->st_connection->spd.this.client.maskbits == 0) + mask = 0; + else + mask = 0xffffffff * 1; + out_raw(&mask,4,&attrval,"IP4_mask"); + } + break; + case INTERNAL_IP4_SUBNET: + { + char mask[4]; + char bits[8] = {0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe}; + int t; + int m = st->st_connection->spd.this.client.maskbits; + + for (t = 0; t < 4; t++) + { + if (m < 8) + mask[t] = bits[m]; + else + mask[t] = 0xff; + m -= 8; + if (m < 0) + m = 0; + } + len = addrbytesptr(&st->st_connection->spd.this.client.addr, &byte_ptr); + out_raw(byte_ptr,len,&attrval,"IP4_subnet"); + out_raw(mask,sizeof(mask),&attrval,"IP4_submsk"); + } + break; + case INTERNAL_IP4_DNS: + len = addrbytesptr(&ia.dns[dns_idx++], &byte_ptr); + out_raw(byte_ptr,len,&attrval,"IP4_dns"); + if (dns_idx < 2 && !isanyaddr(&ia.dns[dns_idx])) + { + dont_advance = TRUE; + } + break; + case INTERNAL_IP4_NBNS: + len = addrbytesptr(&ia.wins[wins_idx++], &byte_ptr); + out_raw(byte_ptr,len,&attrval,"IP4_wins"); + if (wins_idx < 2 && !isanyaddr(&ia.wins[wins_idx])) + { + dont_advance = TRUE; + } + break; + default: + plog("attempt to send unsupported mode cfg attribute %s." + , enum_show(&modecfg_attr_names, attr_type)); + break; + } + close_output_pbs(&attrval); + + } + if (!dont_advance) + { + attr_type++; + resp >>= 1; + } + } + close_message(&strattr); + } + + mode_cfg_hash(r_hashval,r_hash_start,rbody->cur,st); + close_message(rbody); + encrypt_message(rbody, st); + return STF_OK; +} + +/* Set MODE_CONFIG data to client. + * Pack IP Addresses, DNS, etc... and ship + */ +stf_status modecfg_send_set(struct state *st) +{ + pb_stream reply,rbody; + char buf[256]; + + /* set up reply */ + init_pbs(&reply, buf, sizeof(buf), "ModecfgR1"); + + st->st_state = STATE_MODE_CFG_R1; + /* HDR out */ + { + struct isakmp_hdr hdr; + + zero(&hdr); /* default to 0 */ + hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION; + hdr.isa_np = ISAKMP_NEXT_HASH; + hdr.isa_xchg = ISAKMP_XCHG_MODE_CFG; + hdr.isa_flags = ISAKMP_FLAG_ENCRYPTION; + memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE); + memcpy(hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE); + hdr.isa_msgid = st->st_msgid; + + if (!out_struct(&hdr, &isakmp_hdr_desc, &reply, &rbody)) + { + return STF_INTERNAL_ERROR; + } + } + +#define MODECFG_SET_ITEM ( LELEM(INTERNAL_IP4_ADDRESS) | LELEM(INTERNAL_IP4_SUBNET) | LELEM(INTERNAL_IP4_NBNS) | LELEM(INTERNAL_IP4_DNS) ) + + modecfg_resp(st, MODECFG_SET_ITEM + , &rbody + , ISAKMP_CFG_SET + , TRUE + , 0/* XXX ID */); +#undef MODECFG_SET_ITEM + + clonetochunk(st->st_tpacket, reply.start + , pbs_offset(&reply), "ModeCfg set"); + + /* Transmit */ + send_packet(st, "ModeCfg set"); + + /* RETRANSMIT if Main, SA_REPLACE if Aggressive */ + if (st->st_event->ev_type != EVENT_RETRANSMIT + && st->st_event->ev_type != EVENT_NULL) + { + delete_event(st); + event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st); + } + + return STF_OK; +} + +/* Set MODE_CONFIG data to client. + * Pack IP Addresses, DNS, etc... and ship + */ +stf_status +modecfg_start_set(struct state *st) +{ + if (st->st_msgid == 0) + { + /* pick a new message id */ + st->st_msgid = generate_msgid(st); + } + st->st_modecfg.vars_set = TRUE; + + return modecfg_send_set(st); +} + +/* + * Send modecfg IP address request (IP4 address) + */ +stf_status +modecfg_send_request(struct state *st) +{ + pb_stream reply; + pb_stream rbody; + char buf[256]; + u_char *r_hash_start,*r_hashval; + + /* set up reply */ + init_pbs(&reply, buf, sizeof(buf), "modecfg_buf"); + + plog("sending ModeCfg request"); + + /* this is the beginning of a new exchange */ + st->st_msgid = generate_msgid(st); + st->st_state = STATE_MODE_CFG_I1; + + /* HDR out */ + { + struct isakmp_hdr hdr; + + zero(&hdr); /* default to 0 */ + hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION; + hdr.isa_np = ISAKMP_NEXT_HASH; + hdr.isa_xchg = ISAKMP_XCHG_MODE_CFG; + hdr.isa_flags = ISAKMP_FLAG_ENCRYPTION; + memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE); + memcpy(hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE); + hdr.isa_msgid = st->st_msgid; + + if (!out_struct(&hdr, &isakmp_hdr_desc, &reply, &rbody)) + { + return STF_INTERNAL_ERROR; + } + } + + START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_ATTR); + + /* ATTR out */ + { + struct isakmp_mode_attr attrh; + struct isakmp_attribute attr; + pb_stream strattr; + + attrh.isama_np = ISAKMP_NEXT_NONE; + attrh.isama_type = ISAKMP_CFG_REQUEST; + attrh.isama_identifier = 0; + if (!out_struct(&attrh, &isakmp_attr_desc, &rbody, &strattr)) + return STF_INTERNAL_ERROR; + /* ISAKMP attr out (ipv4) */ + attr.isaat_af_type = INTERNAL_IP4_ADDRESS; + attr.isaat_lv = 0; + out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, NULL); + + /* ISAKMP attr out (netmask) */ + attr.isaat_af_type = INTERNAL_IP4_NETMASK; + attr.isaat_lv = 0; + out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, NULL); + + close_message(&strattr); + } + + mode_cfg_hash(r_hashval,r_hash_start,rbody.cur,st); + + close_message(&rbody); + close_output_pbs(&reply); + + init_phase2_iv(st, &st->st_msgid); + encrypt_message(&rbody, st); + + clonetochunk(st->st_tpacket, reply.start, pbs_offset(&reply) + , "modecfg: req"); + + /* Transmit */ + send_packet(st, "modecfg: req"); + + /* RETRANSMIT if Main, SA_REPLACE if Aggressive */ + if (st->st_event->ev_type != EVENT_RETRANSMIT) + { + delete_event(st); + event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0 * 3, st); + } + st->st_modecfg.started = TRUE; + + return STF_OK; +} + +/* + * parse a modecfg attribute payload + */ +static stf_status +modecfg_parse_attributes(pb_stream *attrs, u_int *set) +{ + struct isakmp_attribute attr; + pb_stream strattr; + + while (pbs_left(attrs) > sizeof(struct isakmp_attribute)) + { + if (!in_struct(&attr, &isakmp_modecfg_attribute_desc, attrs, &strattr)) + { + int len = (attr.isaat_af_type & 0x8000)? 4 : attr.isaat_lv; + + if (len < 4) + { + plog("Attribute was too short: %d", len); + return STF_FAIL; + } + + attrs->cur += len; + } + + switch (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ) + { + case INTERNAL_IP4_ADDRESS: + case INTERNAL_IP4_NETMASK: + case INTERNAL_IP4_DNS: + case INTERNAL_IP4_SUBNET: + case INTERNAL_IP4_NBNS: + *set |= LELEM(attr.isaat_af_type); + break; + default: + plog("unsupported mode cfg attribute %s received." + , enum_show(&modecfg_attr_names + , attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK )); + break; + } + } + return STF_OK; +} + +/* STATE_MODE_CFG_R0: + * HDR*, HASH, ATTR(REQ=IP) --> HDR*, HASH, ATTR(REPLY=IP) + * + * This state occurs both in the responder and in the initiator. + * + * In the responding server, it occurs when the client *asks* for an IP + * address or other information. + * + * Otherwise, it occurs in the initiator when the server sends a challenge + * a set, or has a reply to our request. + */ +stf_status +modecfg_inR0(struct msg_digest *md) +{ + struct state *const st = md->st; + struct payload_digest *p; + stf_status stat; + + plog("received ModeCfg request"); + + st->st_msgid = md->hdr.isa_msgid; + CHECK_QUICK_HASH(md, mode_cfg_hash(hash_val + ,hash_pbs->roof + , md->message_pbs.roof, st) + , "MODECFG-HASH", "MODE R0"); + + /* process the MODECFG payloads therein */ + for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next) + { + u_int set_modecfg_attrs = LEMPTY; + + switch (p->payload.attribute.isama_type) + { + default: + plog("Expecting ISAKMP_CFG_REQUEST, got %s instead (ignored)." + , enum_name(&attr_msg_type_names + , p->payload.attribute.isama_type)); + + stat = modecfg_parse_attributes(&p->pbs, &set_modecfg_attrs); + if (stat != STF_OK) + return stat; + break; + + case ISAKMP_CFG_REQUEST: + stat = modecfg_parse_attributes(&p->pbs, &set_modecfg_attrs); + if (stat != STF_OK) + return stat; + + stat = modecfg_resp(st, set_modecfg_attrs + ,&md->rbody + ,ISAKMP_CFG_REPLY + ,TRUE + ,p->payload.attribute.isama_identifier); + + if (stat != STF_OK) + { + /* notification payload - not exactly the right choice, but okay */ + md->note = CERTIFICATE_UNAVAILABLE; + return stat; + } + + /* they asked us, we responded, msgid is done */ + st->st_msgid = 0; + } + } + return STF_OK; +} + +/* STATE_MODE_CFG_R2: + * HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK) + * + * used in server push mode, on the client (initiator). + */ +static stf_status +modecfg_inI2(struct msg_digest *md) +{ + struct state *const st = md->st; + pb_stream *attrs = &md->chain[ISAKMP_NEXT_ATTR]->pbs; + int resp = LEMPTY; + stf_status stat; + struct payload_digest *p; + u_int16_t isama_id = 0; + + st->st_msgid = md->hdr.isa_msgid; + CHECK_QUICK_HASH(md + , mode_cfg_hash(hash_val + ,hash_pbs->roof + , md->message_pbs.roof + , st) + , "MODECFG-HASH", "MODE R1"); + + for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next) + { + struct isakmp_attribute attr; + pb_stream strattr; + + isama_id = p->payload.attribute.isama_identifier; + + if (p->payload.attribute.isama_type != ISAKMP_CFG_SET) + { + plog("Expecting MODE_CFG_SET, got %x instead." + ,md->chain[ISAKMP_NEXT_ATTR]->payload.attribute.isama_type); + return STF_IGNORE; + } + + /* CHECK that SET has been received. */ + + while (pbs_left(attrs) > sizeof(struct isakmp_attribute)) + { + if (!in_struct(&attr, &isakmp_modecfg_attribute_desc + , attrs, &strattr)) + { + int len; + + /* Skip unknown */ + if (attr.isaat_af_type & 0x8000) + len = 4; + else + len = attr.isaat_lv; + + if (len < 4) + { + plog("Attribute was too short: %d", len); + return STF_FAIL; + } + + attrs->cur += len; + } + + switch (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ) + { + case INTERNAL_IP4_ADDRESS: + { + struct connection *c = st->st_connection; + ip_address a; + u_int32_t *ap = (u_int32_t *)(strattr.cur); + a.u.v4.sin_family = AF_INET; + + memcpy(&a.u.v4.sin_addr.s_addr, ap + , sizeof(a.u.v4.sin_addr.s_addr)); + + if (addrbytesptr(&c->spd.this.host_srcip, NULL) == 0 + || isanyaddr(&c->spd.this.host_srcip)) + { + char srcip[ADDRTOT_BUF]; + + c->spd.this.host_srcip = a; + addrtot(&a, 0, srcip, sizeof(srcip)); + plog("setting virtual IP source address to %s", srcip); + } + + /* setting client subnet as srcip/32 */ + addrtosubnet(&a, &c->spd.this.client); + c->spd.this.has_client = TRUE; + } + resp |= LELEM(attr.isaat_af_type); + break; + case INTERNAL_IP4_NETMASK: + case INTERNAL_IP4_DNS: + case INTERNAL_IP4_SUBNET: + case INTERNAL_IP4_NBNS: + resp |= LELEM(attr.isaat_af_type); + break; + default: + plog("unsupported mode cfg attribute %s received." + , enum_show(&modecfg_attr_names, (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ))); + break; + } + } + } + + /* ack things */ + stat = modecfg_resp(st, resp + ,&md->rbody + ,ISAKMP_CFG_ACK + ,FALSE + ,isama_id); + + if (stat != STF_OK) + { + /* notification payload - not exactly the right choice, but okay */ + md->note = CERTIFICATE_UNAVAILABLE; + return stat; + } + + /* + * we are done with this exchange, clear things so + * that we can start phase 2 properly + */ + st->st_msgid = 0; + + if (resp) + { + st->st_modecfg.vars_set = TRUE; + } + return STF_OK; +} + +/* STATE_MODE_CFG_R1: + * HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK) + */ +stf_status +modecfg_inR1(struct msg_digest *md) +{ + struct state *const st = md->st; + pb_stream *attrs = &md->chain[ISAKMP_NEXT_ATTR]->pbs; + int set_modecfg_attrs = LEMPTY; + stf_status stat; + struct payload_digest *p; + + plog("parsing ModeCfg reply"); + + st->st_msgid = md->hdr.isa_msgid; + CHECK_QUICK_HASH(md, mode_cfg_hash(hash_val,hash_pbs->roof, md->message_pbs.roof, st) + , "MODECFG-HASH", "MODE R1"); + + + /* process the MODECFG payloads therein */ + for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next) + { + struct isakmp_attribute attr; + pb_stream strattr; + + attrs = &p->pbs; + + switch (p->payload.attribute.isama_type) + { + default: + { + plog("Expecting MODE_CFG_ACK, got %x instead." + ,md->chain[ISAKMP_NEXT_ATTR]->payload.attribute.isama_type); + return STF_IGNORE; + } + break; + + case ISAKMP_CFG_ACK: + /* CHECK that ACK has been received. */ + stat = modecfg_parse_attributes(attrs, &set_modecfg_attrs); + if (stat != STF_OK) + return stat; + break; + + case ISAKMP_CFG_REPLY: + while (pbs_left(attrs) > sizeof(struct isakmp_attribute)) + { + if (!in_struct(&attr, &isakmp_modecfg_attribute_desc + , attrs, &strattr)) + { + /* Skip unknown */ + int len; + if (attr.isaat_af_type & 0x8000) + len = 4; + else + len = attr.isaat_lv; + + if (len < 4) + { + plog("Attribute was too short: %d", len); + return STF_FAIL; + } + + attrs->cur += len; + } + + switch (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ) + { + case INTERNAL_IP4_ADDRESS: + { + struct connection *c = st->st_connection; + ip_address a; + u_int32_t *ap = (u_int32_t *)(strattr.cur); + a.u.v4.sin_family = AF_INET; + + memcpy(&a.u.v4.sin_addr.s_addr, ap + , sizeof(a.u.v4.sin_addr.s_addr)); + + if (addrbytesptr(&c->spd.this.host_srcip, NULL) == 0 + || isanyaddr(&c->spd.this.host_srcip)) + { + char srcip[ADDRTOT_BUF]; + + c->spd.this.host_srcip = a; + addrtot(&a, 0, srcip, sizeof(srcip)); + plog("setting virtual IP source address to %s", srcip); + } + + /* setting client subnet as srcip/32 */ + addrtosubnet(&a, &c->spd.this.client); + setportof(0, &c->spd.this.client.addr); + c->spd.this.has_client = TRUE; + } + /* fall through to set attribute flage */ + + case INTERNAL_IP4_NETMASK: + case INTERNAL_IP4_DNS: + case INTERNAL_IP4_SUBNET: + case INTERNAL_IP4_NBNS: + set_modecfg_attrs |= LELEM(attr.isaat_af_type); + break; + default: + plog("unsupported mode cfg attribute %s received." + , enum_show(&modecfg_attr_names + , (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ))); + break; + } + } + break; + } + } + + /* we are done with this exchange, clear things so that we can start phase 2 properly */ + st->st_msgid = 0; + + if (set_modecfg_attrs) + { + st->st_modecfg.vars_set = TRUE; + } + return STF_OK; +} |