| 1 |
|
| 2 /* |
|
| 3 Meanwhile - Unofficial Lotus Sametime Community Client Library |
|
| 4 Copyright (C) 2004 Christopher (siege) O'Brien |
|
| 5 |
|
| 6 This library is free software; you can redistribute it and/or |
|
| 7 modify it under the terms of the GNU Library General Public |
|
| 8 License as published by the Free Software Foundation; either |
|
| 9 version 2 of the License, or (at your option) any later version. |
|
| 10 |
|
| 11 This library is distributed in the hope that it will be useful, |
|
| 12 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
| 14 Library General Public License for more details. |
|
| 15 |
|
| 16 You should have received a copy of the GNU Library General Public |
|
| 17 License along with this library; if not, write to the Free |
|
| 18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| 19 */ |
|
| 20 |
|
| 21 #include <glib.h> |
|
| 22 #include <glib/ghash.h> |
|
| 23 #include <glib/glist.h> |
|
| 24 #include <string.h> |
|
| 25 |
|
| 26 #include "mw_channel.h" |
|
| 27 #include "mw_cipher.h" |
|
| 28 #include "mw_debug.h" |
|
| 29 #include "mw_error.h" |
|
| 30 #include "mw_message.h" |
|
| 31 #include "mw_service.h" |
|
| 32 #include "mw_session.h" |
|
| 33 #include "mw_util.h" |
|
| 34 |
|
| 35 |
|
| 36 /** @todo reorganize this file, stuff is just strewn about */ |
|
| 37 |
|
| 38 |
|
| 39 struct mwChannel { |
|
| 40 |
|
| 41 /** session this channel belongs to */ |
|
| 42 struct mwSession *session; |
|
| 43 |
|
| 44 enum mwChannelState state; |
|
| 45 |
|
| 46 /** creator for incoming channel, target for outgoing channel */ |
|
| 47 struct mwLoginInfo user; |
|
| 48 |
|
| 49 /* similar to data from the CreateCnl message in 8.4.1.7 */ |
|
| 50 guint32 reserved; /**< special, unknown meaning */ |
|
| 51 guint32 id; /**< channel ID */ |
|
| 52 guint32 service; /**< service ID */ |
|
| 53 guint32 proto_type; /**< service protocol type */ |
|
| 54 guint32 proto_ver; /**< service protocol version */ |
|
| 55 guint32 options; /**< channel options */ |
|
| 56 |
|
| 57 struct mwOpaque addtl_create; |
|
| 58 struct mwOpaque addtl_accept; |
|
| 59 |
|
| 60 /** all those supported ciphers */ |
|
| 61 GHashTable *supported; |
|
| 62 guint16 offered_policy; /**< @see enum mwEncryptPolicy */ |
|
| 63 guint16 policy; /**< @see enum mwEncryptPolicy */ |
|
| 64 |
|
| 65 /** cipher information determined at channel acceptance */ |
|
| 66 struct mwCipherInstance *cipher; |
|
| 67 |
|
| 68 /** statistics table */ |
|
| 69 GHashTable *stats; |
|
| 70 |
|
| 71 GSList *outgoing_queue; /**< queued outgoing messages */ |
|
| 72 GSList *incoming_queue; /**< queued incoming messages */ |
|
| 73 |
|
| 74 struct mw_datum srvc_data; /**< service-specific data */ |
|
| 75 }; |
|
| 76 |
|
| 77 |
|
| 78 struct mwChannelSet { |
|
| 79 struct mwSession *session; /**< owning session */ |
|
| 80 GHashTable *map; /**< map of all channels, by ID */ |
|
| 81 guint32 counter; /**< counter for outgoing ID */ |
|
| 82 }; |
|
| 83 |
|
| 84 |
|
| 85 static void flush_channel(struct mwChannel *); |
|
| 86 |
|
| 87 |
|
| 88 static const char *state_str(enum mwChannelState state) { |
|
| 89 switch(state) { |
|
| 90 case mwChannel_NEW: return "new"; |
|
| 91 case mwChannel_INIT: return "initializing"; |
|
| 92 case mwChannel_WAIT: return "waiting"; |
|
| 93 case mwChannel_OPEN: return "open"; |
|
| 94 case mwChannel_DESTROY: return "closing"; |
|
| 95 case mwChannel_ERROR: return "error"; |
|
| 96 |
|
| 97 case mwChannel_UNKNOWN: /* fall through */ |
|
| 98 default: return "UNKNOWN"; |
|
| 99 } |
|
| 100 } |
|
| 101 |
|
| 102 |
|
| 103 static void state(struct mwChannel *chan, enum mwChannelState state, |
|
| 104 guint32 err_code) { |
|
| 105 |
|
| 106 g_return_if_fail(chan != NULL); |
|
| 107 |
|
| 108 if(chan->state == state) return; |
|
| 109 |
|
| 110 chan->state = state; |
|
| 111 |
|
| 112 if(err_code) { |
|
| 113 g_message("channel 0x%08x state: %s (0x%08x)", |
|
| 114 chan->id, state_str(state), err_code); |
|
| 115 } else { |
|
| 116 g_message("channel 0x%08x state: %s", chan->id, state_str(state)); |
|
| 117 } |
|
| 118 } |
|
| 119 |
|
| 120 |
|
| 121 static gpointer get_stat(struct mwChannel *chan, |
|
| 122 enum mwChannelStatField field) { |
|
| 123 |
|
| 124 return g_hash_table_lookup(chan->stats, (gpointer) field); |
|
| 125 } |
|
| 126 |
|
| 127 |
|
| 128 static void set_stat(struct mwChannel *chan, enum mwChannelStatField field, |
|
| 129 gpointer val) { |
|
| 130 |
|
| 131 g_hash_table_insert(chan->stats, (gpointer) field, val); |
|
| 132 } |
|
| 133 |
|
| 134 |
|
| 135 #define incr_stat(chan, field, incr) \ |
|
| 136 set_stat(chan, field, get_stat(chan, field) + incr) |
|
| 137 |
|
| 138 |
|
| 139 #define timestamp_stat(chan, field) \ |
|
| 140 set_stat(chan, field, (gpointer) time(NULL)) |
|
| 141 |
|
| 142 |
|
| 143 static void sup_free(gpointer a) { |
|
| 144 mwCipherInstance_free(a); |
|
| 145 } |
|
| 146 |
|
| 147 |
|
| 148 struct mwCipherInstance *get_supported(struct mwChannel *chan, guint16 id) { |
|
| 149 guint32 cid = (guint32) id; |
|
| 150 return g_hash_table_lookup(chan->supported, GUINT_TO_POINTER(cid)); |
|
| 151 } |
|
| 152 |
|
| 153 |
|
| 154 void put_supported(struct mwChannel *chan, struct mwCipherInstance *ci) { |
|
| 155 struct mwCipher *cipher = mwCipherInstance_getCipher(ci); |
|
| 156 guint32 cid = (guint32) mwCipher_getType(cipher); |
|
| 157 g_hash_table_insert(chan->supported, GUINT_TO_POINTER(cid), ci); |
|
| 158 } |
|
| 159 |
|
| 160 |
|
| 161 struct mwChannel *mwChannel_newIncoming(struct mwChannelSet *cs, guint32 id) { |
|
| 162 struct mwChannel *chan; |
|
| 163 |
|
| 164 g_return_val_if_fail(cs != NULL, NULL); |
|
| 165 g_return_val_if_fail(cs->session != NULL, NULL); |
|
| 166 |
|
| 167 chan = g_new0(struct mwChannel, 1); |
|
| 168 chan->state = mwChannel_NEW; |
|
| 169 chan->session = cs->session; |
|
| 170 chan->id = id; |
|
| 171 |
|
| 172 chan->stats = g_hash_table_new(g_direct_hash, g_direct_equal); |
|
| 173 |
|
| 174 chan->supported = g_hash_table_new_full(g_direct_hash, g_direct_equal, |
|
| 175 NULL, sup_free); |
|
| 176 |
|
| 177 g_hash_table_insert(cs->map, GUINT_TO_POINTER(id), chan); |
|
| 178 |
|
| 179 state(chan, mwChannel_WAIT, 0); |
|
| 180 |
|
| 181 return chan; |
|
| 182 } |
|
| 183 |
|
| 184 |
|
| 185 struct mwChannel *mwChannel_newOutgoing(struct mwChannelSet *cs) { |
|
| 186 guint32 id; |
|
| 187 struct mwChannel *chan; |
|
| 188 |
|
| 189 g_return_val_if_fail(cs != NULL, NULL); |
|
| 190 g_return_val_if_fail(cs->map != NULL, NULL); |
|
| 191 |
|
| 192 /* grab the next id, and try to make sure there isn't already a |
|
| 193 channel using it */ |
|
| 194 do { |
|
| 195 id = ++cs->counter; |
|
| 196 } while(g_hash_table_lookup(cs->map, GUINT_TO_POINTER(id))); |
|
| 197 |
|
| 198 chan = mwChannel_newIncoming(cs, id); |
|
| 199 state(chan, mwChannel_INIT, 0); |
|
| 200 |
|
| 201 return chan; |
|
| 202 } |
|
| 203 |
|
| 204 |
|
| 205 guint32 mwChannel_getId(struct mwChannel *chan) { |
|
| 206 g_return_val_if_fail(chan != NULL, 0); |
|
| 207 return chan->id; |
|
| 208 } |
|
| 209 |
|
| 210 |
|
| 211 struct mwSession *mwChannel_getSession(struct mwChannel *chan) { |
|
| 212 g_return_val_if_fail(chan != NULL, NULL); |
|
| 213 return chan->session; |
|
| 214 } |
|
| 215 |
|
| 216 |
|
| 217 guint32 mwChannel_getServiceId(struct mwChannel *chan) { |
|
| 218 g_return_val_if_fail(chan != NULL, 0); |
|
| 219 return chan->service; |
|
| 220 } |
|
| 221 |
|
| 222 |
|
| 223 struct mwService *mwChannel_getService(struct mwChannel *chan) { |
|
| 224 g_return_val_if_fail(chan != NULL, NULL); |
|
| 225 return mwSession_getService(chan->session, chan->service); |
|
| 226 } |
|
| 227 |
|
| 228 |
|
| 229 void mwChannel_setService(struct mwChannel *chan, struct mwService *srvc) { |
|
| 230 g_return_if_fail(chan != NULL); |
|
| 231 g_return_if_fail(srvc != NULL); |
|
| 232 g_return_if_fail(chan->state == mwChannel_INIT); |
|
| 233 chan->service = mwService_getType(srvc); |
|
| 234 } |
|
| 235 |
|
| 236 |
|
| 237 gpointer mwChannel_getServiceData(struct mwChannel *chan) { |
|
| 238 g_return_val_if_fail(chan != NULL, NULL); |
|
| 239 return mw_datum_get(&chan->srvc_data); |
|
| 240 } |
|
| 241 |
|
| 242 |
|
| 243 void mwChannel_setServiceData(struct mwChannel *chan, |
|
| 244 gpointer data, GDestroyNotify clean) { |
|
| 245 |
|
| 246 g_return_if_fail(chan != NULL); |
|
| 247 mw_datum_set(&chan->srvc_data, data, clean); |
|
| 248 } |
|
| 249 |
|
| 250 |
|
| 251 void mwChannel_removeServiceData(struct mwChannel *chan) { |
|
| 252 g_return_if_fail(chan != NULL); |
|
| 253 mw_datum_clear(&chan->srvc_data); |
|
| 254 } |
|
| 255 |
|
| 256 |
|
| 257 guint32 mwChannel_getProtoType(struct mwChannel *chan) { |
|
| 258 g_return_val_if_fail(chan != NULL, 0x00); |
|
| 259 return chan->proto_type; |
|
| 260 } |
|
| 261 |
|
| 262 |
|
| 263 void mwChannel_setProtoType(struct mwChannel *chan, guint32 proto_type) { |
|
| 264 g_return_if_fail(chan != NULL); |
|
| 265 g_return_if_fail(chan->state == mwChannel_INIT); |
|
| 266 chan->proto_type = proto_type; |
|
| 267 } |
|
| 268 |
|
| 269 |
|
| 270 guint32 mwChannel_getProtoVer(struct mwChannel *chan) { |
|
| 271 g_return_val_if_fail(chan != NULL, 0x00); |
|
| 272 return chan->proto_ver; |
|
| 273 } |
|
| 274 |
|
| 275 |
|
| 276 void mwChannel_setProtoVer(struct mwChannel *chan, guint32 proto_ver) { |
|
| 277 g_return_if_fail(chan != NULL); |
|
| 278 g_return_if_fail(chan->state == mwChannel_INIT); |
|
| 279 chan->proto_ver = proto_ver; |
|
| 280 } |
|
| 281 |
|
| 282 |
|
| 283 guint16 mwChannel_getEncryptPolicy(struct mwChannel *chan) { |
|
| 284 g_return_val_if_fail(chan != NULL, 0x00); |
|
| 285 return chan->policy; |
|
| 286 } |
|
| 287 |
|
| 288 |
|
| 289 guint32 mwChannel_getOptions(struct mwChannel *chan) { |
|
| 290 g_return_val_if_fail(chan != NULL, 0x00); |
|
| 291 return chan->options; |
|
| 292 } |
|
| 293 |
|
| 294 |
|
| 295 void mwChannel_setOptions(struct mwChannel *chan, guint32 options) { |
|
| 296 g_return_if_fail(chan != NULL); |
|
| 297 g_return_if_fail(chan->state == mwChannel_INIT); |
|
| 298 chan->options = options; |
|
| 299 } |
|
| 300 |
|
| 301 |
|
| 302 struct mwLoginInfo *mwChannel_getUser(struct mwChannel *chan) { |
|
| 303 g_return_val_if_fail(chan != NULL, NULL); |
|
| 304 return &chan->user; |
|
| 305 } |
|
| 306 |
|
| 307 |
|
| 308 struct mwOpaque *mwChannel_getAddtlCreate(struct mwChannel *chan) { |
|
| 309 g_return_val_if_fail(chan != NULL, NULL); |
|
| 310 return &chan->addtl_create; |
|
| 311 } |
|
| 312 |
|
| 313 |
|
| 314 struct mwOpaque *mwChannel_getAddtlAccept(struct mwChannel *chan) { |
|
| 315 g_return_val_if_fail(chan != NULL, NULL); |
|
| 316 return &chan->addtl_accept; |
|
| 317 } |
|
| 318 |
|
| 319 |
|
| 320 struct mwCipherInstance *mwChannel_getCipherInstance(struct mwChannel *chan) { |
|
| 321 g_return_val_if_fail(chan != NULL, NULL); |
|
| 322 return chan->cipher; |
|
| 323 } |
|
| 324 |
|
| 325 |
|
| 326 enum mwChannelState mwChannel_getState(struct mwChannel *chan) { |
|
| 327 g_return_val_if_fail(chan != NULL, mwChannel_UNKNOWN); |
|
| 328 return chan->state; |
|
| 329 } |
|
| 330 |
|
| 331 |
|
| 332 gpointer mwChannel_getStatistic(struct mwChannel *chan, |
|
| 333 enum mwChannelStatField stat) { |
|
| 334 |
|
| 335 g_return_val_if_fail(chan != NULL, 0); |
|
| 336 g_return_val_if_fail(chan->stats != NULL, 0); |
|
| 337 |
|
| 338 return get_stat(chan, stat); |
|
| 339 } |
|
| 340 |
|
| 341 |
|
| 342 /* send a channel create message */ |
|
| 343 int mwChannel_create(struct mwChannel *chan) { |
|
| 344 struct mwMsgChannelCreate *msg; |
|
| 345 GList *list, *l; |
|
| 346 int ret; |
|
| 347 |
|
| 348 g_return_val_if_fail(chan != NULL, -1); |
|
| 349 g_return_val_if_fail(chan->state == mwChannel_INIT, -1); |
|
| 350 g_return_val_if_fail(mwChannel_isOutgoing(chan), -1); |
|
| 351 |
|
| 352 msg = (struct mwMsgChannelCreate *) |
|
| 353 mwMessage_new(mwMessage_CHANNEL_CREATE); |
|
| 354 |
|
| 355 msg->channel = chan->id; |
|
| 356 msg->target.user = g_strdup(chan->user.user_id); |
|
| 357 msg->target.community = g_strdup(chan->user.community); |
|
| 358 msg->service = chan->service; |
|
| 359 msg->proto_type = chan->proto_type; |
|
| 360 msg->proto_ver = chan->proto_ver; |
|
| 361 msg->options = chan->options; |
|
| 362 mwOpaque_clone(&msg->addtl, &chan->addtl_create); |
|
| 363 |
|
| 364 list = mwChannel_getSupportedCipherInstances(chan); |
|
| 365 if(list) { |
|
| 366 /* offer what we have */ |
|
| 367 for(l = list; l; l = l->next) { |
|
| 368 struct mwEncryptItem *ei = mwCipherInstance_offer(l->data); |
|
| 369 msg->encrypt.items = g_list_append(msg->encrypt.items, ei); |
|
| 370 } |
|
| 371 |
|
| 372 /* we're easy to get along with */ |
|
| 373 chan->offered_policy = mwEncrypt_WHATEVER; |
|
| 374 g_list_free(list); |
|
| 375 |
|
| 376 } else { |
|
| 377 /* we apparently don't support anything */ |
|
| 378 chan->offered_policy = mwEncrypt_NONE; |
|
| 379 } |
|
| 380 |
|
| 381 msg->encrypt.mode = chan->offered_policy; |
|
| 382 msg->encrypt.extra = chan->offered_policy; |
|
| 383 |
|
| 384 ret = mwSession_send(chan->session, MW_MESSAGE(msg)); |
|
| 385 mwMessage_free(MW_MESSAGE(msg)); |
|
| 386 |
|
| 387 state(chan, (ret)? mwChannel_ERROR: mwChannel_WAIT, ret); |
|
| 388 |
|
| 389 return ret; |
|
| 390 } |
|
| 391 |
|
| 392 |
|
| 393 static void channel_open(struct mwChannel *chan) { |
|
| 394 state(chan, mwChannel_OPEN, 0); |
|
| 395 timestamp_stat(chan, mwChannelStat_OPENED_AT); |
|
| 396 flush_channel(chan); |
|
| 397 } |
|
| 398 |
|
| 399 |
|
| 400 int mwChannel_accept(struct mwChannel *chan) { |
|
| 401 struct mwSession *session; |
|
| 402 struct mwMsgChannelAccept *msg; |
|
| 403 struct mwCipherInstance *ci; |
|
| 404 |
|
| 405 int ret; |
|
| 406 |
|
| 407 g_return_val_if_fail(chan != NULL, -1); |
|
| 408 g_return_val_if_fail(mwChannel_isIncoming(chan), -1); |
|
| 409 g_return_val_if_fail(chan->state == mwChannel_WAIT, -1); |
|
| 410 |
|
| 411 session = chan->session; |
|
| 412 g_return_val_if_fail(session != NULL, -1); |
|
| 413 |
|
| 414 msg = (struct mwMsgChannelAccept *) |
|
| 415 mwMessage_new(mwMessage_CHANNEL_ACCEPT); |
|
| 416 |
|
| 417 msg->head.channel = chan->id; |
|
| 418 msg->service = chan->service; |
|
| 419 msg->proto_type = chan->proto_type; |
|
| 420 msg->proto_ver = chan->proto_ver; |
|
| 421 mwOpaque_clone(&msg->addtl, &chan->addtl_accept); |
|
| 422 |
|
| 423 ci = chan->cipher; |
|
| 424 |
|
| 425 if(! ci) { |
|
| 426 /* automatically select a cipher if one hasn't been already */ |
|
| 427 |
|
| 428 switch(chan->offered_policy) { |
|
| 429 case mwEncrypt_NONE: |
|
| 430 mwChannel_selectCipherInstance(chan, NULL); |
|
| 431 break; |
|
| 432 |
|
| 433 case mwEncrypt_RC2_40: |
|
| 434 ci = get_supported(chan, mwCipher_RC2_40); |
|
| 435 mwChannel_selectCipherInstance(chan, ci); |
|
| 436 break; |
|
| 437 |
|
| 438 case mwEncrypt_RC2_128: |
|
| 439 ci = get_supported(chan, mwCipher_RC2_128); |
|
| 440 mwChannel_selectCipherInstance(chan, ci); |
|
| 441 break; |
|
| 442 |
|
| 443 case mwEncrypt_WHATEVER: |
|
| 444 case mwEncrypt_ALL: |
|
| 445 default: |
|
| 446 { |
|
| 447 GList *l, *ll; |
|
| 448 |
|
| 449 l = mwChannel_getSupportedCipherInstances(chan); |
|
| 450 if(l) { |
|
| 451 /* nobody selected a cipher, so we'll just pick the last in |
|
| 452 the list of available ones */ |
|
| 453 for(ll = l; ll->next; ll = ll->next); |
|
| 454 ci = ll->data; |
|
| 455 g_list_free(l); |
|
| 456 |
|
| 457 mwChannel_selectCipherInstance(chan, ci); |
|
| 458 |
|
| 459 } else { |
|
| 460 /* this may cause breakage, but there's really nothing else |
|
| 461 we can do. They want something we can't provide. If they |
|
| 462 don't like it, then they'll error the channel out */ |
|
| 463 mwChannel_selectCipherInstance(chan, NULL); |
|
| 464 } |
|
| 465 } |
|
| 466 } |
|
| 467 } |
|
| 468 |
|
| 469 msg->encrypt.mode = chan->policy; /* set in selectCipherInstance */ |
|
| 470 msg->encrypt.extra = chan->offered_policy; |
|
| 471 |
|
| 472 if(chan->cipher) { |
|
| 473 msg->encrypt.item = mwCipherInstance_accept(chan->cipher); |
|
| 474 } |
|
| 475 |
|
| 476 ret = mwSession_send(session, MW_MESSAGE(msg)); |
|
| 477 mwMessage_free(MW_MESSAGE(msg)); |
|
| 478 |
|
| 479 if(ret) { |
|
| 480 state(chan, mwChannel_ERROR, ret); |
|
| 481 } else { |
|
| 482 channel_open(chan); |
|
| 483 } |
|
| 484 |
|
| 485 return ret; |
|
| 486 } |
|
| 487 |
|
| 488 |
|
| 489 static void channel_free(struct mwChannel *chan) { |
|
| 490 struct mwSession *s; |
|
| 491 struct mwMessage *msg; |
|
| 492 GSList *l; |
|
| 493 |
|
| 494 /* maybe no warning in the future */ |
|
| 495 g_return_if_fail(chan != NULL); |
|
| 496 |
|
| 497 s = chan->session; |
|
| 498 |
|
| 499 mwLoginInfo_clear(&chan->user); |
|
| 500 mwOpaque_clear(&chan->addtl_create); |
|
| 501 mwOpaque_clear(&chan->addtl_accept); |
|
| 502 |
|
| 503 if(chan->supported) { |
|
| 504 g_hash_table_destroy(chan->supported); |
|
| 505 chan->supported = NULL; |
|
| 506 } |
|
| 507 |
|
| 508 if(chan->stats) { |
|
| 509 g_hash_table_destroy(chan->stats); |
|
| 510 chan->stats = NULL; |
|
| 511 } |
|
| 512 |
|
| 513 mwCipherInstance_free(chan->cipher); |
|
| 514 |
|
| 515 /* clean up the outgoing queue */ |
|
| 516 for(l = chan->outgoing_queue; l; l = l->next) { |
|
| 517 msg = (struct mwMessage *) l->data; |
|
| 518 l->data = NULL; |
|
| 519 mwMessage_free(msg); |
|
| 520 } |
|
| 521 g_slist_free(chan->outgoing_queue); |
|
| 522 |
|
| 523 /* clean up the incoming queue */ |
|
| 524 for(l = chan->incoming_queue; l; l = l->next) { |
|
| 525 msg = (struct mwMessage *) l->data; |
|
| 526 l->data = NULL; |
|
| 527 mwMessage_free(msg); |
|
| 528 } |
|
| 529 g_slist_free(chan->incoming_queue); |
|
| 530 |
|
| 531 g_free(chan); |
|
| 532 } |
|
| 533 |
|
| 534 |
|
| 535 int mwChannel_destroy(struct mwChannel *chan, |
|
| 536 guint32 reason, struct mwOpaque *info) { |
|
| 537 |
|
| 538 struct mwMsgChannelDestroy *msg; |
|
| 539 struct mwSession *session; |
|
| 540 struct mwChannelSet *cs; |
|
| 541 int ret; |
|
| 542 |
|
| 543 /* may make this not a warning in the future */ |
|
| 544 g_return_val_if_fail(chan != NULL, 0); |
|
| 545 |
|
| 546 state(chan, reason? mwChannel_ERROR: mwChannel_DESTROY, reason); |
|
| 547 |
|
| 548 session = chan->session; |
|
| 549 g_return_val_if_fail(session != NULL, -1); |
|
| 550 |
|
| 551 cs = mwSession_getChannels(session); |
|
| 552 g_return_val_if_fail(cs != NULL, -1); |
|
| 553 |
|
| 554 /* compose the message */ |
|
| 555 msg = (struct mwMsgChannelDestroy *) |
|
| 556 mwMessage_new(mwMessage_CHANNEL_DESTROY); |
|
| 557 msg->head.channel = chan->id; |
|
| 558 msg->reason = reason; |
|
| 559 if(info) mwOpaque_clone(&msg->data, info); |
|
| 560 |
|
| 561 /* remove the channel from the channel set */ |
|
| 562 g_hash_table_remove(cs->map, GUINT_TO_POINTER(chan->id)); |
|
| 563 |
|
| 564 /* send the message */ |
|
| 565 ret = mwSession_send(session, (struct mwMessage *) msg); |
|
| 566 mwMessage_free(MW_MESSAGE(msg)); |
|
| 567 |
|
| 568 return ret; |
|
| 569 } |
|
| 570 |
|
| 571 |
|
| 572 static void queue_outgoing(struct mwChannel *chan, |
|
| 573 struct mwMsgChannelSend *msg) { |
|
| 574 |
|
| 575 g_info("queue_outgoing, channel 0x%08x", chan->id); |
|
| 576 chan->outgoing_queue = g_slist_append(chan->outgoing_queue, msg); |
|
| 577 } |
|
| 578 |
|
| 579 |
|
| 580 static int channel_send(struct mwChannel *chan, |
|
| 581 struct mwMsgChannelSend *msg) { |
|
| 582 |
|
| 583 int ret = 0; |
|
| 584 |
|
| 585 /* if the channel is open, send and free the message. Otherwise, |
|
| 586 queue the message to be sent once the channel is finally |
|
| 587 opened */ |
|
| 588 |
|
| 589 if(chan->state == mwChannel_OPEN) { |
|
| 590 ret = mwSession_send(chan->session, (struct mwMessage *) msg); |
|
| 591 mwMessage_free(MW_MESSAGE(msg)); |
|
| 592 |
|
| 593 } else { |
|
| 594 queue_outgoing(chan, msg); |
|
| 595 } |
|
| 596 |
|
| 597 return ret; |
|
| 598 } |
|
| 599 |
|
| 600 |
|
| 601 int mwChannel_sendEncrypted(struct mwChannel *chan, |
|
| 602 guint32 type, struct mwOpaque *data, |
|
| 603 gboolean encrypt) { |
|
| 604 |
|
| 605 struct mwMsgChannelSend *msg; |
|
| 606 |
|
| 607 g_return_val_if_fail(chan != NULL, -1); |
|
| 608 |
|
| 609 msg = (struct mwMsgChannelSend *) mwMessage_new(mwMessage_CHANNEL_SEND); |
|
| 610 msg->head.channel = chan->id; |
|
| 611 msg->type = type; |
|
| 612 |
|
| 613 mwOpaque_clone(&msg->data, data); |
|
| 614 |
|
| 615 if(encrypt && chan->cipher) { |
|
| 616 msg->head.options = mwMessageOption_ENCRYPT; |
|
| 617 mwCipherInstance_encrypt(chan->cipher, &msg->data); |
|
| 618 } |
|
| 619 |
|
| 620 return channel_send(chan, msg); |
|
| 621 } |
|
| 622 |
|
| 623 |
|
| 624 int mwChannel_send(struct mwChannel *chan, guint32 type, |
|
| 625 struct mwOpaque *data) { |
|
| 626 |
|
| 627 return mwChannel_sendEncrypted(chan, type, data, TRUE); |
|
| 628 } |
|
| 629 |
|
| 630 |
|
| 631 static void queue_incoming(struct mwChannel *chan, |
|
| 632 struct mwMsgChannelSend *msg) { |
|
| 633 |
|
| 634 /* we clone the message, because session_process will clear it once |
|
| 635 we return */ |
|
| 636 |
|
| 637 struct mwMsgChannelSend *m = g_new0(struct mwMsgChannelSend, 1); |
|
| 638 m->head.type = msg->head.type; |
|
| 639 m->head.options = msg->head.options; |
|
| 640 m->head.channel = msg->head.channel; |
|
| 641 mwOpaque_clone(&m->head.attribs, &msg->head.attribs); |
|
| 642 |
|
| 643 m->type = msg->type; |
|
| 644 mwOpaque_clone(&m->data, &msg->data); |
|
| 645 |
|
| 646 g_info("queue_incoming, channel 0x%08x", chan->id); |
|
| 647 chan->incoming_queue = g_slist_append(chan->incoming_queue, m); |
|
| 648 } |
|
| 649 |
|
| 650 |
|
| 651 static void channel_recv(struct mwChannel *chan, |
|
| 652 struct mwMsgChannelSend *msg) { |
|
| 653 |
|
| 654 struct mwService *srvc; |
|
| 655 srvc = mwChannel_getService(chan); |
|
| 656 |
|
| 657 incr_stat(chan, mwChannelStat_MSG_RECV, 1); |
|
| 658 |
|
| 659 if(msg->head.options & mwMessageOption_ENCRYPT) { |
|
| 660 struct mwOpaque data = { 0, 0 }; |
|
| 661 mwOpaque_clone(&data, &msg->data); |
|
| 662 |
|
| 663 mwCipherInstance_decrypt(chan->cipher, &data); |
|
| 664 mwService_recv(srvc, chan, msg->type, &data); |
|
| 665 mwOpaque_clear(&data); |
|
| 666 |
|
| 667 } else { |
|
| 668 mwService_recv(srvc, chan, msg->type, &msg->data); |
|
| 669 } |
|
| 670 } |
|
| 671 |
|
| 672 |
|
| 673 static void flush_channel(struct mwChannel *chan) { |
|
| 674 GSList *l; |
|
| 675 |
|
| 676 for(l = chan->incoming_queue; l; l = l->next) { |
|
| 677 struct mwMsgChannelSend *msg = (struct mwMsgChannelSend *) l->data; |
|
| 678 l->data = NULL; |
|
| 679 |
|
| 680 channel_recv(chan, msg); |
|
| 681 mwMessage_free(MW_MESSAGE(msg)); |
|
| 682 } |
|
| 683 g_slist_free(chan->incoming_queue); |
|
| 684 chan->incoming_queue = NULL; |
|
| 685 |
|
| 686 for(l = chan->outgoing_queue; l; l = l->next) { |
|
| 687 struct mwMessage *msg = (struct mwMessage *) l->data; |
|
| 688 l->data = NULL; |
|
| 689 |
|
| 690 mwSession_send(chan->session, msg); |
|
| 691 mwMessage_free(msg); |
|
| 692 } |
|
| 693 g_slist_free(chan->outgoing_queue); |
|
| 694 chan->outgoing_queue = NULL; |
|
| 695 } |
|
| 696 |
|
| 697 |
|
| 698 void mwChannel_recv(struct mwChannel *chan, struct mwMsgChannelSend *msg) { |
|
| 699 if(chan->state == mwChannel_OPEN) { |
|
| 700 channel_recv(chan, msg); |
|
| 701 |
|
| 702 } else { |
|
| 703 queue_incoming(chan, msg); |
|
| 704 } |
|
| 705 } |
|
| 706 |
|
| 707 |
|
| 708 struct mwChannel *mwChannel_find(struct mwChannelSet *cs, guint32 chan) { |
|
| 709 g_return_val_if_fail(cs != NULL, NULL); |
|
| 710 g_return_val_if_fail(cs->map != NULL, NULL); |
|
| 711 return g_hash_table_lookup(cs->map, GUINT_TO_POINTER(chan)); |
|
| 712 } |
|
| 713 |
|
| 714 |
|
| 715 void mwChannelSet_free(struct mwChannelSet *cs) { |
|
| 716 if(! cs) return; |
|
| 717 if(cs->map) g_hash_table_destroy(cs->map); |
|
| 718 g_free(cs); |
|
| 719 } |
|
| 720 |
|
| 721 |
|
| 722 struct mwChannelSet *mwChannelSet_new(struct mwSession *s) { |
|
| 723 struct mwChannelSet *cs = g_new0(struct mwChannelSet, 1); |
|
| 724 cs->session = s; |
|
| 725 |
|
| 726 /* for some reason, g_int_hash/g_int_equal cause a SIGSEGV */ |
|
| 727 cs->map = g_hash_table_new_full(g_direct_hash, g_direct_equal, |
|
| 728 NULL, (GDestroyNotify) channel_free); |
|
| 729 return cs; |
|
| 730 } |
|
| 731 |
|
| 732 |
|
| 733 void mwChannel_recvCreate(struct mwChannel *chan, |
|
| 734 struct mwMsgChannelCreate *msg) { |
|
| 735 |
|
| 736 struct mwSession *session; |
|
| 737 GList *list; |
|
| 738 struct mwService *srvc; |
|
| 739 |
|
| 740 g_return_if_fail(chan != NULL); |
|
| 741 g_return_if_fail(msg != NULL); |
|
| 742 g_return_if_fail(chan->id == msg->channel); |
|
| 743 |
|
| 744 session = chan->session; |
|
| 745 g_return_if_fail(session != NULL); |
|
| 746 |
|
| 747 if(mwChannel_isOutgoing(chan)) { |
|
| 748 g_warning("channel 0x%08x not an incoming channel", chan->id); |
|
| 749 mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL); |
|
| 750 return; |
|
| 751 } |
|
| 752 |
|
| 753 chan->offered_policy = msg->encrypt.mode; |
|
| 754 g_message("channel offered with encrypt policy 0x%04x", chan->policy); |
|
| 755 |
|
| 756 for(list = msg->encrypt.items; list; list = list->next) { |
|
| 757 struct mwEncryptItem *ei = list->data; |
|
| 758 struct mwCipher *cipher; |
|
| 759 struct mwCipherInstance *ci; |
|
| 760 |
|
| 761 g_message("channel offered cipher id 0x%04x", ei->id); |
|
| 762 cipher = mwSession_getCipher(session, ei->id); |
|
| 763 if(! cipher) { |
|
| 764 g_message("no such cipher found in session"); |
|
| 765 continue; |
|
| 766 } |
|
| 767 |
|
| 768 ci = mwCipher_newInstance(cipher, chan); |
|
| 769 mwCipherInstance_offered(ci, ei); |
|
| 770 mwChannel_addSupportedCipherInstance(chan, ci); |
|
| 771 } |
|
| 772 |
|
| 773 mwLoginInfo_clone(&chan->user, &msg->creator); |
|
| 774 chan->service = msg->service; |
|
| 775 chan->proto_type = msg->proto_type; |
|
| 776 chan->proto_ver = msg->proto_ver; |
|
| 777 |
|
| 778 srvc = mwSession_getService(session, msg->service); |
|
| 779 if(srvc) { |
|
| 780 mwService_recvCreate(srvc, chan, msg); |
|
| 781 |
|
| 782 } else { |
|
| 783 mwChannel_destroy(chan, ERR_SERVICE_NO_SUPPORT, NULL); |
|
| 784 } |
|
| 785 } |
|
| 786 |
|
| 787 |
|
| 788 void mwChannel_recvAccept(struct mwChannel *chan, |
|
| 789 struct mwMsgChannelAccept *msg) { |
|
| 790 |
|
| 791 struct mwService *srvc; |
|
| 792 |
|
| 793 g_return_if_fail(chan != NULL); |
|
| 794 g_return_if_fail(msg != NULL); |
|
| 795 g_return_if_fail(chan->id == msg->head.channel); |
|
| 796 |
|
| 797 if(mwChannel_isIncoming(chan)) { |
|
| 798 g_warning("channel 0x%08x not an outgoing channel", chan->id); |
|
| 799 mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL); |
|
| 800 return; |
|
| 801 } |
|
| 802 |
|
| 803 if(chan->state != mwChannel_WAIT) { |
|
| 804 g_warning("channel 0x%08x state not WAIT: %s", |
|
| 805 chan->id, state_str(chan->state)); |
|
| 806 mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL); |
|
| 807 return; |
|
| 808 } |
|
| 809 |
|
| 810 mwLoginInfo_clone(&chan->user, &msg->acceptor); |
|
| 811 |
|
| 812 srvc = mwSession_getService(chan->session, chan->service); |
|
| 813 if(! srvc) { |
|
| 814 g_warning("no service: 0x%08x", chan->service); |
|
| 815 mwChannel_destroy(chan, ERR_SERVICE_NO_SUPPORT, NULL); |
|
| 816 return; |
|
| 817 } |
|
| 818 |
|
| 819 chan->policy = msg->encrypt.mode; |
|
| 820 g_message("channel accepted with encrypt policy 0x%04x", chan->policy); |
|
| 821 |
|
| 822 if(! msg->encrypt.mode || ! msg->encrypt.item) { |
|
| 823 /* no mode or no item means no encryption */ |
|
| 824 mwChannel_selectCipherInstance(chan, NULL); |
|
| 825 |
|
| 826 } else { |
|
| 827 guint16 cid = msg->encrypt.item->id; |
|
| 828 struct mwCipherInstance *ci = get_supported(chan, cid); |
|
| 829 |
|
| 830 if(! ci) { |
|
| 831 g_warning("not an offered cipher: 0x%04x", cid); |
|
| 832 mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL); |
|
| 833 return; |
|
| 834 } |
|
| 835 |
|
| 836 mwCipherInstance_accepted(ci, msg->encrypt.item); |
|
| 837 mwChannel_selectCipherInstance(chan, ci); |
|
| 838 } |
|
| 839 |
|
| 840 /* mark it as open for the service */ |
|
| 841 state(chan, mwChannel_OPEN, 0); |
|
| 842 |
|
| 843 /* let the service know */ |
|
| 844 mwService_recvAccept(srvc, chan, msg); |
|
| 845 |
|
| 846 /* flush it if the service didn't just immediately close it */ |
|
| 847 if(mwChannel_isState(chan, mwChannel_OPEN)) { |
|
| 848 channel_open(chan); |
|
| 849 } |
|
| 850 } |
|
| 851 |
|
| 852 |
|
| 853 void mwChannel_recvDestroy(struct mwChannel *chan, |
|
| 854 struct mwMsgChannelDestroy *msg) { |
|
| 855 |
|
| 856 struct mwChannelSet *cs; |
|
| 857 struct mwService *srvc; |
|
| 858 |
|
| 859 g_return_if_fail(chan != NULL); |
|
| 860 g_return_if_fail(msg != NULL); |
|
| 861 g_return_if_fail(chan->id == msg->head.channel); |
|
| 862 |
|
| 863 state(chan, msg->reason? mwChannel_ERROR: mwChannel_DESTROY, msg->reason); |
|
| 864 |
|
| 865 srvc = mwChannel_getService(chan); |
|
| 866 if(srvc) mwService_recvDestroy(srvc, chan, msg); |
|
| 867 |
|
| 868 cs = mwSession_getChannels(chan->session); |
|
| 869 g_return_if_fail(cs != NULL); |
|
| 870 g_return_if_fail(cs->map != NULL); |
|
| 871 |
|
| 872 g_hash_table_remove(cs->map, GUINT_TO_POINTER(chan->id)); |
|
| 873 } |
|
| 874 |
|
| 875 |
|
| 876 void mwChannel_populateSupportedCipherInstances(struct mwChannel *chan) { |
|
| 877 struct mwSession *session; |
|
| 878 GList *list; |
|
| 879 |
|
| 880 g_return_if_fail(chan != NULL); |
|
| 881 |
|
| 882 session = chan->session; |
|
| 883 g_return_if_fail(session != NULL); |
|
| 884 |
|
| 885 for(list = mwSession_getCiphers(session); list; list = list->next) { |
|
| 886 struct mwCipherInstance *ci = mwCipher_newInstance(list->data, chan); |
|
| 887 if(! ci) continue; |
|
| 888 put_supported(chan, ci); |
|
| 889 } |
|
| 890 } |
|
| 891 |
|
| 892 |
|
| 893 void mwChannel_addSupportedCipherInstance(struct mwChannel *chan, |
|
| 894 struct mwCipherInstance *ci) { |
|
| 895 g_return_if_fail(chan != NULL); |
|
| 896 g_message("channel 0x%08x added cipher %s", chan->id, |
|
| 897 NSTR(mwCipher_getName(mwCipherInstance_getCipher(ci)))); |
|
| 898 put_supported(chan, ci); |
|
| 899 } |
|
| 900 |
|
| 901 |
|
| 902 static void collect(gpointer a, gpointer b, gpointer c) { |
|
| 903 GList **list = c; |
|
| 904 *list = g_list_append(*list, b); |
|
| 905 } |
|
| 906 |
|
| 907 |
|
| 908 GList *mwChannel_getSupportedCipherInstances(struct mwChannel *chan) { |
|
| 909 GList *list = NULL; |
|
| 910 |
|
| 911 g_return_val_if_fail(chan != NULL, NULL); |
|
| 912 g_hash_table_foreach(chan->supported, collect, &list); |
|
| 913 |
|
| 914 return list; |
|
| 915 } |
|
| 916 |
|
| 917 |
|
| 918 void mwChannel_selectCipherInstance(struct mwChannel *chan, |
|
| 919 struct mwCipherInstance *ci) { |
|
| 920 struct mwCipher *c; |
|
| 921 |
|
| 922 g_return_if_fail(chan != NULL); |
|
| 923 g_return_if_fail(chan->supported != NULL); |
|
| 924 |
|
| 925 chan->cipher = ci; |
|
| 926 if(ci) { |
|
| 927 guint cid; |
|
| 928 |
|
| 929 c = mwCipherInstance_getCipher(ci); |
|
| 930 cid = mwCipher_getType(c); |
|
| 931 |
|
| 932 g_hash_table_steal(chan->supported, GUINT_TO_POINTER(cid)); |
|
| 933 |
|
| 934 switch(mwCipher_getType(c)) { |
|
| 935 case mwCipher_RC2_40: |
|
| 936 chan->policy = mwEncrypt_RC2_40; |
|
| 937 break; |
|
| 938 |
|
| 939 case mwCipher_RC2_128: |
|
| 940 chan->policy = mwEncrypt_RC2_128; |
|
| 941 break; |
|
| 942 |
|
| 943 default: |
|
| 944 /* unsure if this is bad */ |
|
| 945 chan->policy = mwEncrypt_WHATEVER; |
|
| 946 } |
|
| 947 |
|
| 948 g_message("channel 0x%08x selected cipher %s", |
|
| 949 chan->id, NSTR(mwCipher_getName(c))); |
|
| 950 |
|
| 951 } else { |
|
| 952 |
|
| 953 chan->policy = mwEncrypt_NONE; |
|
| 954 g_message("channel 0x%08x selected no cipher", chan->id); |
|
| 955 } |
|
| 956 |
|
| 957 g_hash_table_destroy(chan->supported); |
|
| 958 chan->supported = NULL; |
|
| 959 } |
|
| 960 |
|
| 961 |
|