Blame src/channel.c

Packit 16808d
Packit 16808d
/*
Packit 16808d
  Meanwhile - Unofficial Lotus Sametime Community Client Library
Packit 16808d
  Copyright (C) 2004  Christopher (siege) O'Brien
Packit 16808d
  
Packit 16808d
  This library is free software; you can redistribute it and/or
Packit 16808d
  modify it under the terms of the GNU Library General Public
Packit 16808d
  License as published by the Free Software Foundation; either
Packit 16808d
  version 2 of the License, or (at your option) any later version.
Packit 16808d
  
Packit 16808d
  This library is distributed in the hope that it will be useful,
Packit 16808d
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 16808d
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 16808d
  Library General Public License for more details.
Packit 16808d
  
Packit 16808d
  You should have received a copy of the GNU Library General Public
Packit 16808d
  License along with this library; if not, write to the Free
Packit 16808d
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Packit 16808d
*/
Packit 16808d
Packit 16808d
#include <glib.h>
Packit 16808d
#include <string.h>
Packit 16808d
Packit 16808d
#include "mw_channel.h"
Packit 16808d
#include "mw_cipher.h"
Packit 16808d
#include "mw_debug.h"
Packit 16808d
#include "mw_error.h"
Packit 16808d
#include "mw_message.h"
Packit 16808d
#include "mw_service.h"
Packit 16808d
#include "mw_session.h"
Packit 16808d
#include "mw_util.h"
Packit 16808d
Packit 16808d
Packit 16808d
/** @todo reorganize this file, stuff is just strewn about */
Packit 16808d
Packit 16808d
Packit 16808d
struct mwChannel {
Packit 16808d
Packit 16808d
  /** session this channel belongs to */
Packit 16808d
  struct mwSession *session;
Packit 16808d
Packit 16808d
  enum mwChannelState state;
Packit 16808d
Packit 16808d
  /** creator for incoming channel, target for outgoing channel */
Packit 16808d
  struct mwLoginInfo user;
Packit 16808d
Packit 16808d
  /* similar to data from the CreateCnl message in 8.4.1.7 */
Packit 16808d
  guint32 reserved;    /**< special, unknown meaning */
Packit 16808d
  guint32 id;          /**< channel ID */
Packit 16808d
  guint32 service;     /**< service ID */
Packit 16808d
  guint32 proto_type;  /**< service protocol type */
Packit 16808d
  guint32 proto_ver;   /**< service protocol version */
Packit 16808d
  guint32 options;     /**< channel options */
Packit 16808d
Packit 16808d
  struct mwOpaque addtl_create;
Packit 16808d
  struct mwOpaque addtl_accept;
Packit 16808d
Packit 16808d
  /** all those supported ciphers */
Packit 16808d
  GHashTable *supported;
Packit 16808d
  guint16 offered_policy;  /**< @see enum mwEncryptPolicy */
Packit 16808d
  guint16 policy;          /**< @see enum mwEncryptPolicy */
Packit 16808d
Packit 16808d
  /** cipher information determined at channel acceptance */
Packit 16808d
  struct mwCipherInstance *cipher;
Packit 16808d
Packit 16808d
  /** statistics table */
Packit 16808d
  GHashTable *stats;
Packit 16808d
Packit 16808d
  GSList *outgoing_queue;     /**< queued outgoing messages */
Packit 16808d
  GSList *incoming_queue;     /**< queued incoming messages */
Packit 16808d
Packit 16808d
  struct mw_datum srvc_data;  /**< service-specific data */
Packit 16808d
};
Packit 16808d
Packit 16808d
Packit 16808d
struct mwChannelSet {
Packit 16808d
  struct mwSession *session;  /**< owning session */
Packit 16808d
  GHashTable *map;            /**< map of all channels, by ID */
Packit 16808d
  guint32 counter;            /**< counter for outgoing ID */
Packit 16808d
};
Packit 16808d
Packit 16808d
Packit 16808d
static void flush_channel(struct mwChannel *);
Packit 16808d
Packit 16808d
Packit 16808d
static const char *state_str(enum mwChannelState state) {
Packit 16808d
  switch(state) {
Packit 16808d
  case mwChannel_NEW:      return "new";
Packit 16808d
  case mwChannel_INIT:     return "initializing";
Packit 16808d
  case mwChannel_WAIT:     return "waiting";
Packit 16808d
  case mwChannel_OPEN:     return "open";
Packit 16808d
  case mwChannel_DESTROY:  return "closing";
Packit 16808d
  case mwChannel_ERROR:    return "error";
Packit 16808d
Packit 16808d
  case mwChannel_UNKNOWN:  /* fall through */
Packit 16808d
  default:                 return "UNKNOWN";
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void state(struct mwChannel *chan, enum mwChannelState state,
Packit 16808d
		  guint32 err_code) {
Packit 16808d
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
Packit 16808d
  if(chan->state == state) return;
Packit 16808d
Packit 16808d
  chan->state = state;
Packit 16808d
Packit 16808d
  if(err_code) {
Packit 16808d
    g_message("channel 0x%08x state: %s (0x%08x)",
Packit 16808d
	      chan->id, state_str(state), err_code);
Packit 16808d
  } else {
Packit 16808d
    g_message("channel 0x%08x state: %s", chan->id, state_str(state));
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static gpointer get_stat(struct mwChannel *chan,
Packit 16808d
			 enum mwChannelStatField field) {
Packit 16808d
Packit 16808d
  return g_hash_table_lookup(chan->stats, (gpointer) field);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void set_stat(struct mwChannel *chan, enum mwChannelStatField field,
Packit 16808d
		     gpointer val) {
Packit 16808d
Packit 16808d
  g_hash_table_insert(chan->stats, (gpointer) field, val);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
#define incr_stat(chan, field, incr) \
Packit 16808d
  set_stat(chan, field, get_stat(chan, field) + incr)
Packit 16808d
Packit 16808d
Packit 16808d
#define timestamp_stat(chan, field) \
Packit 16808d
  set_stat(chan, field, (gpointer) time(NULL))
Packit 16808d
Packit 16808d
Packit 16808d
static void sup_free(gpointer a) {
Packit 16808d
  mwCipherInstance_free(a);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static struct mwCipherInstance *
Packit 16808d
get_supported(struct mwChannel *chan, guint16 id) {
Packit 16808d
Packit 16808d
  guint32 cid = (guint32) id;
Packit 16808d
  return g_hash_table_lookup(chan->supported, GUINT_TO_POINTER(cid));
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void put_supported(struct mwChannel *chan,
Packit 16808d
			  struct mwCipherInstance *ci) {
Packit 16808d
Packit 16808d
  struct mwCipher *cipher = mwCipherInstance_getCipher(ci);
Packit 16808d
  guint32 cid = (guint32) mwCipher_getType(cipher);
Packit 16808d
  g_hash_table_insert(chan->supported, GUINT_TO_POINTER(cid), ci);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwChannel *mwChannel_newIncoming(struct mwChannelSet *cs, guint32 id) {
Packit 16808d
  struct mwChannel *chan;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(cs != NULL, NULL);
Packit 16808d
  g_return_val_if_fail(cs->session != NULL, NULL);
Packit 16808d
Packit 16808d
  chan = g_new0(struct mwChannel, 1);
Packit 16808d
  chan->state = mwChannel_NEW;
Packit 16808d
  chan->session = cs->session;
Packit 16808d
  chan->id = id;
Packit 16808d
Packit 16808d
  chan->stats = g_hash_table_new(g_direct_hash, g_direct_equal);
Packit 16808d
Packit 16808d
  chan->supported = g_hash_table_new_full(g_direct_hash, g_direct_equal,
Packit 16808d
					  NULL, sup_free);
Packit 16808d
Packit 16808d
  g_hash_table_insert(cs->map, GUINT_TO_POINTER(id), chan);
Packit 16808d
Packit 16808d
  state(chan, mwChannel_WAIT, 0);
Packit 16808d
Packit 16808d
  return chan;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwChannel *mwChannel_newOutgoing(struct mwChannelSet *cs) {
Packit 16808d
  guint32 id;
Packit 16808d
  struct mwChannel *chan;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(cs != NULL, NULL);
Packit 16808d
  g_return_val_if_fail(cs->map != NULL, NULL);
Packit 16808d
Packit 16808d
  /* grab the next id, and try to make sure there isn't already a
Packit 16808d
     channel using it */
Packit 16808d
  do {
Packit 16808d
    id = ++cs->counter;
Packit 16808d
  } while(g_hash_table_lookup(cs->map, GUINT_TO_POINTER(id)));
Packit 16808d
  
Packit 16808d
  chan = mwChannel_newIncoming(cs, id);
Packit 16808d
  state(chan, mwChannel_INIT, 0);
Packit 16808d
Packit 16808d
  return chan;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
guint32 mwChannel_getId(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, 0);
Packit 16808d
  return chan->id;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwSession *mwChannel_getSession(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, NULL);
Packit 16808d
  return chan->session;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
guint32 mwChannel_getServiceId(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, 0);
Packit 16808d
  return chan->service;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwService *mwChannel_getService(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, NULL);
Packit 16808d
  return mwSession_getService(chan->session, chan->service);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_setService(struct mwChannel *chan, struct mwService *srvc) {
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
  g_return_if_fail(srvc != NULL);
Packit 16808d
  g_return_if_fail(chan->state == mwChannel_INIT);
Packit 16808d
  chan->service = mwService_getType(srvc);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
gpointer mwChannel_getServiceData(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, NULL);
Packit 16808d
  return mw_datum_get(&chan->srvc_data);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_setServiceData(struct mwChannel *chan,
Packit 16808d
			      gpointer data, GDestroyNotify clean) {
Packit 16808d
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
  mw_datum_set(&chan->srvc_data, data, clean);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_removeServiceData(struct mwChannel *chan) {
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
  mw_datum_clear(&chan->srvc_data);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
guint32 mwChannel_getProtoType(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, 0x00);
Packit 16808d
  return chan->proto_type;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_setProtoType(struct mwChannel *chan, guint32 proto_type) {
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
  g_return_if_fail(chan->state == mwChannel_INIT);
Packit 16808d
  chan->proto_type = proto_type;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
guint32 mwChannel_getProtoVer(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, 0x00);
Packit 16808d
  return chan->proto_ver;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_setProtoVer(struct mwChannel *chan, guint32 proto_ver) {
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
  g_return_if_fail(chan->state == mwChannel_INIT);
Packit 16808d
  chan->proto_ver = proto_ver;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
guint16 mwChannel_getEncryptPolicy(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, 0x00);
Packit 16808d
  return chan->policy;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
guint32 mwChannel_getOptions(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, 0x00);
Packit 16808d
  return chan->options;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_setOptions(struct mwChannel *chan, guint32 options) {
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
  g_return_if_fail(chan->state == mwChannel_INIT);
Packit 16808d
  chan->options = options;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwLoginInfo *mwChannel_getUser(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, NULL);
Packit 16808d
  return &chan->user;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwOpaque *mwChannel_getAddtlCreate(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, NULL);
Packit 16808d
  return &chan->addtl_create;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwOpaque *mwChannel_getAddtlAccept(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, NULL);
Packit 16808d
  return &chan->addtl_accept;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwCipherInstance *
Packit 16808d
mwChannel_getCipherInstance(struct mwChannel *chan) {
Packit 16808d
Packit 16808d
  g_return_val_if_fail(chan != NULL, NULL);
Packit 16808d
  return chan->cipher;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
enum mwChannelState mwChannel_getState(struct mwChannel *chan) {
Packit 16808d
  g_return_val_if_fail(chan != NULL, mwChannel_UNKNOWN);
Packit 16808d
  return chan->state;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
gpointer mwChannel_getStatistic(struct mwChannel *chan,
Packit 16808d
				enum mwChannelStatField stat) {
Packit 16808d
  
Packit 16808d
  g_return_val_if_fail(chan != NULL, 0);
Packit 16808d
  g_return_val_if_fail(chan->stats != NULL, 0);
Packit 16808d
Packit 16808d
  return get_stat(chan, stat);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
/* send a channel create message */
Packit 16808d
int mwChannel_create(struct mwChannel *chan) {
Packit 16808d
  struct mwMsgChannelCreate *msg;
Packit 16808d
  GList *list, *l;
Packit 16808d
  int ret;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(chan != NULL, -1);
Packit 16808d
  g_return_val_if_fail(chan->state == mwChannel_INIT, -1);
Packit 16808d
  g_return_val_if_fail(mwChannel_isOutgoing(chan), -1);
Packit 16808d
Packit 16808d
  msg = (struct mwMsgChannelCreate *)
Packit 16808d
    mwMessage_new(mwMessage_CHANNEL_CREATE);
Packit 16808d
Packit 16808d
  msg->channel = chan->id;
Packit 16808d
  msg->target.user = g_strdup(chan->user.user_id);
Packit 16808d
  msg->target.community = g_strdup(chan->user.community);
Packit 16808d
  msg->service = chan->service;
Packit 16808d
  msg->proto_type = chan->proto_type;
Packit 16808d
  msg->proto_ver = chan->proto_ver;
Packit 16808d
  msg->options = chan->options;
Packit 16808d
  mwOpaque_clone(&msg->addtl, &chan->addtl_create);
Packit 16808d
Packit 16808d
  list = mwChannel_getSupportedCipherInstances(chan);
Packit 16808d
  if(list) {
Packit 16808d
    /* offer what we have */
Packit 16808d
    for(l = list; l; l = l->next) {
Packit 16808d
      struct mwEncryptItem *ei = mwCipherInstance_offer(l->data);
Packit 16808d
      msg->encrypt.items = g_list_append(msg->encrypt.items, ei);
Packit 16808d
    }
Packit 16808d
Packit 16808d
    /* we're easy to get along with */
Packit 16808d
    chan->offered_policy = mwEncrypt_WHATEVER;
Packit 16808d
    g_list_free(list);
Packit 16808d
Packit 16808d
  } else {
Packit 16808d
    /* we apparently don't support anything */
Packit 16808d
    chan->offered_policy = mwEncrypt_NONE;
Packit 16808d
  }
Packit 16808d
Packit 16808d
  msg->encrypt.mode = chan->offered_policy;
Packit 16808d
  msg->encrypt.extra = chan->offered_policy;
Packit 16808d
  
Packit 16808d
  ret = mwSession_send(chan->session, MW_MESSAGE(msg));
Packit 16808d
  mwMessage_free(MW_MESSAGE(msg));
Packit 16808d
Packit 16808d
  state(chan, (ret)? mwChannel_ERROR: mwChannel_WAIT, ret);
Packit 16808d
Packit 16808d
  return ret;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void channel_open(struct mwChannel *chan) {
Packit 16808d
  state(chan, mwChannel_OPEN, 0);
Packit 16808d
  timestamp_stat(chan, mwChannelStat_OPENED_AT);
Packit 16808d
  flush_channel(chan);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
int mwChannel_accept(struct mwChannel *chan) {
Packit 16808d
  struct mwSession *session;
Packit 16808d
  struct mwMsgChannelAccept *msg;
Packit 16808d
  struct mwCipherInstance *ci;
Packit 16808d
Packit 16808d
  int ret;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(chan != NULL, -1);
Packit 16808d
  g_return_val_if_fail(mwChannel_isIncoming(chan), -1);
Packit 16808d
  g_return_val_if_fail(chan->state == mwChannel_WAIT, -1);
Packit 16808d
Packit 16808d
  session = chan->session;
Packit 16808d
  g_return_val_if_fail(session != NULL, -1);
Packit 16808d
Packit 16808d
  msg = (struct mwMsgChannelAccept *)
Packit 16808d
    mwMessage_new(mwMessage_CHANNEL_ACCEPT);
Packit 16808d
Packit 16808d
  msg->head.channel = chan->id;
Packit 16808d
  msg->service = chan->service;
Packit 16808d
  msg->proto_type = chan->proto_type;
Packit 16808d
  msg->proto_ver = chan->proto_ver;
Packit 16808d
  mwOpaque_clone(&msg->addtl, &chan->addtl_accept);
Packit 16808d
Packit 16808d
  ci = chan->cipher;
Packit 16808d
Packit 16808d
  if(! ci) {
Packit 16808d
    /* automatically select a cipher if one hasn't been already */
Packit 16808d
Packit 16808d
    switch(chan->offered_policy) {
Packit 16808d
    case mwEncrypt_NONE:
Packit 16808d
      mwChannel_selectCipherInstance(chan, NULL);
Packit 16808d
      break;
Packit 16808d
      
Packit 16808d
    case mwEncrypt_RC2_40:
Packit 16808d
      ci = get_supported(chan, mwCipher_RC2_40);
Packit 16808d
      mwChannel_selectCipherInstance(chan, ci);
Packit 16808d
      break;
Packit 16808d
Packit 16808d
    case mwEncrypt_RC2_128:
Packit 16808d
      ci = get_supported(chan, mwCipher_RC2_128);
Packit 16808d
      mwChannel_selectCipherInstance(chan, ci);
Packit 16808d
      break;
Packit 16808d
      
Packit 16808d
    case mwEncrypt_WHATEVER:
Packit 16808d
    case mwEncrypt_ALL:
Packit 16808d
    default:
Packit 16808d
      {
Packit 16808d
	GList *l, *ll;
Packit 16808d
Packit 16808d
	l = mwChannel_getSupportedCipherInstances(chan);
Packit 16808d
	if(l) {
Packit 16808d
	  /* nobody selected a cipher, so we'll just pick the last in
Packit 16808d
	     the list of available ones */
Packit 16808d
	  for(ll = l; ll->next; ll = ll->next);
Packit 16808d
	  ci = ll->data;
Packit 16808d
	  g_list_free(l);
Packit 16808d
	  
Packit 16808d
	  mwChannel_selectCipherInstance(chan, ci);
Packit 16808d
	  
Packit 16808d
	} else {
Packit 16808d
	  /* this may cause breakage, but there's really nothing else
Packit 16808d
	     we can do. They want something we can't provide. If they
Packit 16808d
	     don't like it, then they'll error the channel out */
Packit 16808d
	  mwChannel_selectCipherInstance(chan, NULL);
Packit 16808d
	}
Packit 16808d
      }
Packit 16808d
    }
Packit 16808d
  }
Packit 16808d
Packit 16808d
  msg->encrypt.mode = chan->policy; /* set in selectCipherInstance */
Packit 16808d
  msg->encrypt.extra = chan->offered_policy;
Packit 16808d
Packit 16808d
  if(chan->cipher) {
Packit 16808d
    msg->encrypt.item = mwCipherInstance_accept(chan->cipher);
Packit 16808d
  }
Packit 16808d
Packit 16808d
  ret = mwSession_send(session, MW_MESSAGE(msg));
Packit 16808d
  mwMessage_free(MW_MESSAGE(msg));
Packit 16808d
Packit 16808d
  if(ret) {
Packit 16808d
    state(chan, mwChannel_ERROR, ret);
Packit 16808d
  } else {
Packit 16808d
    channel_open(chan);
Packit 16808d
  }
Packit 16808d
Packit 16808d
  return ret;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void channel_free(struct mwChannel *chan) {
Packit 16808d
  struct mwSession *s;
Packit 16808d
  struct mwMessage *msg;
Packit 16808d
  GSList *l;
Packit 16808d
Packit 16808d
  /* maybe no warning in the future */
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
Packit 16808d
  s = chan->session;
Packit 16808d
Packit 16808d
  mwLoginInfo_clear(&chan->user);
Packit 16808d
  mwOpaque_clear(&chan->addtl_create);
Packit 16808d
  mwOpaque_clear(&chan->addtl_accept);
Packit 16808d
Packit 16808d
  if(chan->supported) {
Packit 16808d
    g_hash_table_destroy(chan->supported);
Packit 16808d
    chan->supported = NULL;
Packit 16808d
  }
Packit 16808d
Packit 16808d
  if(chan->stats) {
Packit 16808d
    g_hash_table_destroy(chan->stats);
Packit 16808d
    chan->stats = NULL;
Packit 16808d
  }
Packit 16808d
  
Packit 16808d
  mwCipherInstance_free(chan->cipher);
Packit 16808d
Packit 16808d
  /* clean up the outgoing queue */
Packit 16808d
  for(l = chan->outgoing_queue; l; l = l->next) {
Packit 16808d
    msg = (struct mwMessage *) l->data;
Packit 16808d
    l->data = NULL;
Packit 16808d
    mwMessage_free(msg);
Packit 16808d
  }
Packit 16808d
  g_slist_free(chan->outgoing_queue);
Packit 16808d
Packit 16808d
  /* clean up the incoming queue */
Packit 16808d
  for(l = chan->incoming_queue; l; l = l->next) {
Packit 16808d
    msg = (struct mwMessage *) l->data;
Packit 16808d
    l->data = NULL;
Packit 16808d
    mwMessage_free(msg);
Packit 16808d
  }
Packit 16808d
  g_slist_free(chan->incoming_queue);
Packit 16808d
Packit 16808d
  g_free(chan);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
int mwChannel_destroy(struct mwChannel *chan,
Packit 16808d
		      guint32 reason, struct mwOpaque *info) {
Packit 16808d
Packit 16808d
  struct mwMsgChannelDestroy *msg;
Packit 16808d
  struct mwSession *session;
Packit 16808d
  struct mwChannelSet *cs;
Packit 16808d
  int ret;
Packit 16808d
Packit 16808d
  /* may make this not a warning in the future */
Packit 16808d
  g_return_val_if_fail(chan != NULL, 0);
Packit 16808d
Packit 16808d
  state(chan, reason? mwChannel_ERROR: mwChannel_DESTROY, reason);
Packit 16808d
Packit 16808d
  session = chan->session;
Packit 16808d
  g_return_val_if_fail(session != NULL, -1);
Packit 16808d
Packit 16808d
  cs = mwSession_getChannels(session);
Packit 16808d
  g_return_val_if_fail(cs != NULL, -1);
Packit 16808d
Packit 16808d
  /* compose the message */
Packit 16808d
  msg = (struct mwMsgChannelDestroy *)
Packit 16808d
    mwMessage_new(mwMessage_CHANNEL_DESTROY);
Packit 16808d
  msg->head.channel = chan->id;
Packit 16808d
  msg->reason = reason;
Packit 16808d
  if(info) mwOpaque_clone(&msg->data, info);
Packit 16808d
Packit 16808d
  /* remove the channel from the channel set */
Packit 16808d
  g_hash_table_remove(cs->map, GUINT_TO_POINTER(chan->id));
Packit 16808d
  
Packit 16808d
  /* send the message */
Packit 16808d
  ret = mwSession_send(session, (struct mwMessage *) msg);
Packit 16808d
  mwMessage_free(MW_MESSAGE(msg));
Packit 16808d
Packit 16808d
  return ret;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void queue_outgoing(struct mwChannel *chan,
Packit 16808d
			   struct mwMsgChannelSend *msg) {
Packit 16808d
Packit 16808d
  g_info("queue_outgoing, channel 0x%08x", chan->id);
Packit 16808d
  chan->outgoing_queue = g_slist_append(chan->outgoing_queue, msg);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static int channel_send(struct mwChannel *chan,
Packit 16808d
			struct mwMsgChannelSend *msg) {
Packit 16808d
Packit 16808d
  int ret = 0;
Packit 16808d
Packit 16808d
  /* if the channel is open, send and free the message. Otherwise,
Packit 16808d
     queue the message to be sent once the channel is finally
Packit 16808d
     opened */
Packit 16808d
Packit 16808d
  if(chan->state == mwChannel_OPEN) {
Packit 16808d
    ret = mwSession_send(chan->session, (struct mwMessage *) msg);
Packit 16808d
    mwMessage_free(MW_MESSAGE(msg));
Packit 16808d
Packit 16808d
  } else {
Packit 16808d
    queue_outgoing(chan, msg);
Packit 16808d
  }
Packit 16808d
Packit 16808d
  return ret;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
int mwChannel_sendEncrypted(struct mwChannel *chan,
Packit 16808d
			    guint32 type, struct mwOpaque *data,
Packit 16808d
			    gboolean encrypt) {
Packit 16808d
Packit 16808d
  struct mwMsgChannelSend *msg;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(chan != NULL, -1);
Packit 16808d
Packit 16808d
  msg = (struct mwMsgChannelSend *) mwMessage_new(mwMessage_CHANNEL_SEND);
Packit 16808d
  msg->head.channel = chan->id;
Packit 16808d
  msg->type = type;
Packit 16808d
Packit 16808d
  mwOpaque_clone(&msg->data, data);
Packit 16808d
Packit 16808d
  if(encrypt && chan->cipher) {
Packit 16808d
    msg->head.options = mwMessageOption_ENCRYPT;
Packit 16808d
    mwCipherInstance_encrypt(chan->cipher, &msg->data);
Packit 16808d
  }
Packit 16808d
Packit 16808d
  return channel_send(chan, msg);  
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
int mwChannel_send(struct mwChannel *chan, guint32 type,
Packit 16808d
		   struct mwOpaque *data) {
Packit 16808d
Packit 16808d
  return mwChannel_sendEncrypted(chan, type, data, TRUE);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void queue_incoming(struct mwChannel *chan,
Packit 16808d
			   struct mwMsgChannelSend *msg) {
Packit 16808d
Packit 16808d
  /* we clone the message, because session_process will clear it once
Packit 16808d
     we return */
Packit 16808d
Packit 16808d
  struct mwMsgChannelSend *m = g_new0(struct mwMsgChannelSend, 1);
Packit 16808d
  m->head.type = msg->head.type;
Packit 16808d
  m->head.options = msg->head.options;
Packit 16808d
  m->head.channel = msg->head.channel;
Packit 16808d
  mwOpaque_clone(&m->head.attribs, &msg->head.attribs);
Packit 16808d
Packit 16808d
  m->type = msg->type;
Packit 16808d
  mwOpaque_clone(&m->data, &msg->data);
Packit 16808d
Packit 16808d
  g_info("queue_incoming, channel 0x%08x", chan->id);
Packit 16808d
  chan->incoming_queue = g_slist_append(chan->incoming_queue, m);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void channel_recv(struct mwChannel *chan,
Packit 16808d
			 struct mwMsgChannelSend *msg) {
Packit 16808d
Packit 16808d
  struct mwService *srvc;
Packit 16808d
  srvc = mwChannel_getService(chan);
Packit 16808d
Packit 16808d
  incr_stat(chan, mwChannelStat_MSG_RECV, 1);
Packit 16808d
Packit 16808d
  if(msg->head.options & mwMessageOption_ENCRYPT) {
Packit 16808d
    struct mwOpaque data = { 0, 0 };
Packit 16808d
    mwOpaque_clone(&data, &msg->data);
Packit 16808d
Packit 16808d
    mwCipherInstance_decrypt(chan->cipher, &data);
Packit 16808d
    mwService_recv(srvc, chan, msg->type, &data);
Packit 16808d
    mwOpaque_clear(&data);
Packit 16808d
    
Packit 16808d
  } else {
Packit 16808d
    mwService_recv(srvc, chan, msg->type, &msg->data);
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void flush_channel(struct mwChannel *chan) {
Packit 16808d
  GSList *l;
Packit 16808d
Packit 16808d
  for(l = chan->incoming_queue; l; l = l->next) {
Packit 16808d
    struct mwMsgChannelSend *msg = (struct mwMsgChannelSend *) l->data;
Packit 16808d
    l->data = NULL;
Packit 16808d
Packit 16808d
    channel_recv(chan, msg);
Packit 16808d
    mwMessage_free(MW_MESSAGE(msg));
Packit 16808d
  }
Packit 16808d
  g_slist_free(chan->incoming_queue);
Packit 16808d
  chan->incoming_queue = NULL;
Packit 16808d
Packit 16808d
  for(l = chan->outgoing_queue; l; l = l->next) {
Packit 16808d
    struct mwMessage *msg = (struct mwMessage *) l->data;
Packit 16808d
    l->data = NULL;
Packit 16808d
Packit 16808d
    mwSession_send(chan->session, msg);
Packit 16808d
    mwMessage_free(msg);
Packit 16808d
  }
Packit 16808d
  g_slist_free(chan->outgoing_queue);
Packit 16808d
  chan->outgoing_queue = NULL;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_recv(struct mwChannel *chan, struct mwMsgChannelSend *msg) {
Packit 16808d
  if(chan->state == mwChannel_OPEN) {
Packit 16808d
    channel_recv(chan, msg);
Packit 16808d
Packit 16808d
  } else {
Packit 16808d
    queue_incoming(chan, msg);
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwChannel *mwChannel_find(struct mwChannelSet *cs, guint32 chan) {
Packit 16808d
  g_return_val_if_fail(cs != NULL, NULL);
Packit 16808d
  g_return_val_if_fail(cs->map != NULL, NULL);
Packit 16808d
  return g_hash_table_lookup(cs->map, GUINT_TO_POINTER(chan));
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannelSet_free(struct mwChannelSet *cs) {
Packit 16808d
  if(! cs) return;
Packit 16808d
  if(cs->map) g_hash_table_destroy(cs->map);
Packit 16808d
  g_free(cs);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwChannelSet *mwChannelSet_new(struct mwSession *s) {
Packit 16808d
  struct mwChannelSet *cs = g_new0(struct mwChannelSet, 1);
Packit 16808d
  cs->session = s;
Packit 16808d
Packit 16808d
  /* for some reason, g_int_hash/g_int_equal cause a SIGSEGV */
Packit 16808d
  cs->map = g_hash_table_new_full(g_direct_hash, g_direct_equal,
Packit 16808d
				  NULL, (GDestroyNotify) channel_free);
Packit 16808d
  return cs;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_recvCreate(struct mwChannel *chan,
Packit 16808d
			  struct mwMsgChannelCreate *msg) {
Packit 16808d
Packit 16808d
  struct mwSession *session;
Packit 16808d
  GList *list;
Packit 16808d
  struct mwService *srvc;
Packit 16808d
  
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
  g_return_if_fail(msg != NULL);
Packit 16808d
  g_return_if_fail(chan->id == msg->channel);
Packit 16808d
Packit 16808d
  session = chan->session;
Packit 16808d
  g_return_if_fail(session != NULL);
Packit 16808d
Packit 16808d
  if(mwChannel_isOutgoing(chan)) {
Packit 16808d
    g_warning("channel 0x%08x not an incoming channel", chan->id);
Packit 16808d
    mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL);
Packit 16808d
    return;
Packit 16808d
  }
Packit 16808d
Packit 16808d
  chan->offered_policy = msg->encrypt.mode;
Packit 16808d
  g_message("channel offered with encrypt policy 0x%04x", chan->policy);
Packit 16808d
Packit 16808d
  for(list = msg->encrypt.items; list; list = list->next) {
Packit 16808d
    struct mwEncryptItem *ei = list->data;
Packit 16808d
    struct mwCipher *cipher;
Packit 16808d
    struct mwCipherInstance *ci;
Packit 16808d
Packit 16808d
    g_message("channel offered cipher id 0x%04x", ei->id);
Packit 16808d
    cipher = mwSession_getCipher(session, ei->id);
Packit 16808d
    if(! cipher) {
Packit 16808d
      g_message("no such cipher found in session");
Packit 16808d
      continue;
Packit 16808d
    }
Packit 16808d
Packit 16808d
    ci = mwCipher_newInstance(cipher, chan);
Packit 16808d
    mwCipherInstance_offered(ci, ei);
Packit 16808d
    mwChannel_addSupportedCipherInstance(chan, ci);
Packit 16808d
  }
Packit 16808d
Packit 16808d
  mwLoginInfo_clone(&chan->user, &msg->creator);
Packit 16808d
  chan->service = msg->service;
Packit 16808d
  chan->proto_type = msg->proto_type;
Packit 16808d
  chan->proto_ver = msg->proto_ver;
Packit 16808d
  
Packit 16808d
  srvc = mwSession_getService(session, msg->service);
Packit 16808d
  if(srvc) {
Packit 16808d
    mwService_recvCreate(srvc, chan, msg);
Packit 16808d
Packit 16808d
  } else {
Packit 16808d
    mwChannel_destroy(chan, ERR_SERVICE_NO_SUPPORT, NULL);
Packit 16808d
  }  
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_recvAccept(struct mwChannel *chan,
Packit 16808d
			  struct mwMsgChannelAccept *msg) {
Packit 16808d
Packit 16808d
  struct mwService *srvc;
Packit 16808d
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
  g_return_if_fail(msg != NULL);
Packit 16808d
  g_return_if_fail(chan->id == msg->head.channel);
Packit 16808d
Packit 16808d
  if(mwChannel_isIncoming(chan)) {
Packit 16808d
    g_warning("channel 0x%08x not an outgoing channel", chan->id);
Packit 16808d
    mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL);
Packit 16808d
    return;
Packit 16808d
  }
Packit 16808d
Packit 16808d
  if(chan->state != mwChannel_WAIT) {
Packit 16808d
    g_warning("channel 0x%08x state not WAIT: %s",
Packit 16808d
	      chan->id, state_str(chan->state));
Packit 16808d
    mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL);
Packit 16808d
    return;
Packit 16808d
  }
Packit 16808d
Packit 16808d
  mwLoginInfo_clone(&chan->user, &msg->acceptor);
Packit 16808d
Packit 16808d
  srvc = mwSession_getService(chan->session, chan->service);
Packit 16808d
  if(! srvc) {
Packit 16808d
    g_warning("no service: 0x%08x", chan->service);
Packit 16808d
    mwChannel_destroy(chan, ERR_SERVICE_NO_SUPPORT, NULL);
Packit 16808d
    return;
Packit 16808d
  }
Packit 16808d
Packit 16808d
  chan->policy = msg->encrypt.mode;
Packit 16808d
  g_message("channel accepted with encrypt policy 0x%04x", chan->policy);
Packit 16808d
Packit 16808d
  if(! msg->encrypt.mode || ! msg->encrypt.item) {
Packit 16808d
    /* no mode or no item means no encryption */
Packit 16808d
    mwChannel_selectCipherInstance(chan, NULL);
Packit 16808d
Packit 16808d
  } else {
Packit 16808d
    guint16 cid = msg->encrypt.item->id;
Packit 16808d
    struct mwCipherInstance *ci = get_supported(chan, cid);
Packit 16808d
Packit 16808d
    if(! ci) {
Packit 16808d
      g_warning("not an offered cipher: 0x%04x", cid);
Packit 16808d
      mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL);
Packit 16808d
      return;
Packit 16808d
    }
Packit 16808d
Packit 16808d
    mwCipherInstance_accepted(ci, msg->encrypt.item);
Packit 16808d
    mwChannel_selectCipherInstance(chan, ci);
Packit 16808d
  }
Packit 16808d
Packit 16808d
  /* mark it as open for the service */
Packit 16808d
  state(chan, mwChannel_OPEN, 0);
Packit 16808d
Packit 16808d
  /* let the service know */
Packit 16808d
  mwService_recvAccept(srvc, chan, msg);
Packit 16808d
Packit 16808d
  /* flush it if the service didn't just immediately close it */
Packit 16808d
  if(mwChannel_isState(chan, mwChannel_OPEN)) {
Packit 16808d
    channel_open(chan);
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_recvDestroy(struct mwChannel *chan,
Packit 16808d
			   struct mwMsgChannelDestroy *msg) {
Packit 16808d
Packit 16808d
  struct mwChannelSet *cs;
Packit 16808d
  struct mwService *srvc;
Packit 16808d
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
  g_return_if_fail(msg != NULL);
Packit 16808d
  g_return_if_fail(chan->id == msg->head.channel);
Packit 16808d
Packit 16808d
  state(chan, msg->reason? mwChannel_ERROR: mwChannel_DESTROY, msg->reason);
Packit 16808d
Packit 16808d
  srvc = mwChannel_getService(chan);
Packit 16808d
  if(srvc) mwService_recvDestroy(srvc, chan, msg);
Packit 16808d
Packit 16808d
  cs = mwSession_getChannels(chan->session);
Packit 16808d
  g_return_if_fail(cs != NULL);
Packit 16808d
  g_return_if_fail(cs->map != NULL);
Packit 16808d
Packit 16808d
  g_hash_table_remove(cs->map, GUINT_TO_POINTER(chan->id));
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_populateSupportedCipherInstances(struct mwChannel *chan) {
Packit 16808d
  struct mwSession *session;
Packit 16808d
  GList *list;
Packit 16808d
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
Packit 16808d
  session = chan->session;
Packit 16808d
  g_return_if_fail(session != NULL);
Packit 16808d
Packit 16808d
  for(list = mwSession_getCiphers(session); list; list = list->next) {
Packit 16808d
    struct mwCipherInstance *ci = mwCipher_newInstance(list->data, chan);
Packit 16808d
    if(! ci) continue;
Packit 16808d
    put_supported(chan, ci);
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_addSupportedCipherInstance(struct mwChannel *chan,
Packit 16808d
					  struct mwCipherInstance *ci) {
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
  g_message("channel 0x%08x added cipher %s", chan->id,
Packit 16808d
	    NSTR(mwCipher_getName(mwCipherInstance_getCipher(ci))));
Packit 16808d
  put_supported(chan, ci);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void collect(gpointer a, gpointer b, gpointer c) {
Packit 16808d
  GList **list = c;
Packit 16808d
  *list = g_list_append(*list, b);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
GList *mwChannel_getSupportedCipherInstances(struct mwChannel *chan) {
Packit 16808d
  GList *list = NULL;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(chan != NULL, NULL);
Packit 16808d
  g_hash_table_foreach(chan->supported, collect, &list);
Packit 16808d
Packit 16808d
  return list;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwChannel_selectCipherInstance(struct mwChannel *chan,
Packit 16808d
				    struct mwCipherInstance *ci) {
Packit 16808d
  struct mwCipher *c;
Packit 16808d
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
  g_return_if_fail(chan->supported != NULL);
Packit 16808d
Packit 16808d
  chan->cipher = ci;
Packit 16808d
  if(ci) {
Packit 16808d
    guint cid;
Packit 16808d
Packit 16808d
    c = mwCipherInstance_getCipher(ci);
Packit 16808d
    cid = mwCipher_getType(c);
Packit 16808d
Packit 16808d
    g_hash_table_steal(chan->supported, GUINT_TO_POINTER(cid));
Packit 16808d
Packit 16808d
    switch(mwCipher_getType(c)) {
Packit 16808d
    case mwCipher_RC2_40:
Packit 16808d
      chan->policy = mwEncrypt_RC2_40;
Packit 16808d
      break;
Packit 16808d
Packit 16808d
    case mwCipher_RC2_128:
Packit 16808d
      chan->policy = mwEncrypt_RC2_128;
Packit 16808d
      break;
Packit 16808d
Packit 16808d
    default:
Packit 16808d
      /* unsure if this is bad */
Packit 16808d
      chan->policy = mwEncrypt_WHATEVER;
Packit 16808d
    }
Packit 16808d
Packit 16808d
    g_message("channel 0x%08x selected cipher %s",
Packit 16808d
	      chan->id, NSTR(mwCipher_getName(c)));
Packit 16808d
Packit 16808d
  } else {
Packit 16808d
Packit 16808d
    chan->policy = mwEncrypt_NONE;
Packit 16808d
    g_message("channel 0x%08x selected no cipher", chan->id);
Packit 16808d
  }
Packit 16808d
Packit 16808d
  g_hash_table_destroy(chan->supported);
Packit 16808d
  chan->supported = NULL;
Packit 16808d
}
Packit 16808d
Packit 16808d