转载自: http://blog.csdn.net/songbohr/article/details/5309650
本文立足于rdesktop的架构层次进行解析,算是抛砖引玉,国内对RDP协议深入解析的资料到本文发布时为空白!
ps:昨天在nokia 5233系统下载了一个symRdp,国外RDP的应用已经遍地开花了。
调用层次:
rdp_--->sec_--->mcs_--->iso_--->tcp_
协议包编解码层次:
rdp_hdr->sec_hdr->mcs_hdr->iso_hdr->data,所有这些指针组成一个STREAM.
- /* Parser state */
- typedef struct stream
- {
- unsigned char *p; // 临时指针变量,用于计算定位。
- unsigned char *end; // TCP/IP数据的结束位置
- unsigned char *data; // TCP/IP数据的起始位置,是申请的一段内存,只当数据尺寸大于size时,进行realloc增大,不缩小
- unsigned int size; // TCP/IP数据的尺寸大小
- //以上是tcp数据管理变量
- /* Offsets of various headers */
- unsigned char *iso_hdr; // TCP/IP数据包中iso协议控制头的位置
- unsigned char *mcs_hdr; // TCP/IP数据包中mcs协议控制头的位置
- unsigned char *sec_hdr; // TCP/IP数据包中sec协议控制头的位置
- unsigned char *rdp_hdr; // TCP/IP数据包中rdp协议控制头的位置
- unsigned char *channel_hdr;
- }
- *STREAM;
ISO控制头:7字节
MCS控制头:8字节
SEC控制头:0(如果已经获得许可证—通信协定)、4(未获许可)或12(进行加密时)字节
由SEC头控制的数据段即是RDP的主要数据,一般进行了加密。
主过程:
1、rdp_connect: 按照调用层次依次调用sec_connect……,然后调用rdp_send_logon_info发送登录请求验证信息.其中rdp_send_logon_info调用sec_init初始化数据包,调用sec_send发送数据包,根据flags(包含加密标识)调用加密处理逻辑.
- /* Establish a connection up to the RDP layer */
- BOOL
- rdp_connect(char *server, uint32 flags, char *domain, char *password,
- char *command, char *directory)
- {
- if (!sec_connect(server, g_username))
- return False;
- rdp_send_logon_info(flags, domain, g_username, password, command, directory);
- return True;
- }
2、然后进入rdp_main_loop循环,调用rdp_recv,根据触发的事件类型做相应处理。
- /* Process incoming packets */
- /* nevers gets out of here till app is done */
- void
- rdp_main_loop(BOOL * deactivated, uint32 * ext_disc_reason)
- {
- while (rdp_loop(deactivated, ext_disc_reason))
- ;
- }
- /* used in uiports and rdp_main_loop, processes the rdp packets waiting */
- BOOL
- rdp_loop(BOOL * deactivated, uint32 * ext_disc_reason)
- {
- uint8 type;
- BOOL disc = False; /* True when a disconnect PDU was received */
- BOOL cont = True;
- STREAM s;
- while (cont)
- {
- s = rdp_recv(&type);
- if (s == NULL)
- return False;
- switch (type)
- {
- case RDP_PDU_DEMAND_ACTIVE:
- process_demand_active(s);
- *deactivated = False;
- break;
- case RDP_PDU_DEACTIVATE:
- DEBUG(("RDP_PDU_DEACTIVATE\n"));
- *deactivated = True;
- break;
- case RDP_PDU_REDIRECT:
- return process_redirect_pdu(s);
- break;
- case RDP_PDU_DATA:
- disc = process_data_pdu(s, ext_disc_reason);
- break;
- case 0:
- break;
- default:
- unimpl("PDU %d\n", type);
- }
- if (disc)
- return False;
- cont = g_next_packet < s->end;
- }
- return True;
- }
3、rdp_disconnect,按照调用层次依次调用sec_disconnect……断开。特殊的,在iso_disconnect中首先调用iso_send_msg(ISO_PDU_DR)发送PDU消息包,然后再调用tcp_disconnect 断开连接。
- /* Disconnect from the RDP layer */
- void
- rdp_disconnect(void)
- {
- sec_disconnect();
- }
- /* Disconnect from the ISO layer */
- void
- iso_disconnect(void)
- {
- iso_send_msg(ISO_PDU_DR);
- tcp_disconnect();
- }
- /* ISO PDU codes */
- enum ISO_PDU_CODE
- {
- ISO_PDU_CR = 0xE0, /* Connection Request */
- ISO_PDU_CC = 0xD0, /* Connection Confirm */
- ISO_PDU_DR = 0x80, /* Disconnect Request */
- ISO_PDU_DT = 0xF0, /* Data */
- ISO_PDU_ER = 0x70 /* Error */
- };
- /* MCS PDU codes */
- enum MCS_PDU_TYPE
- {
- MCS_EDRQ = 1, /* Erect Domain Request */
- MCS_DPUM = 8, /* Disconnect Provider Ultimatum */
- MCS_AURQ = 10, /* Attach User Request */
- MCS_AUCF = 11, /* Attach User Confirm */
- MCS_CJRQ = 14, /* Channel Join Request */
- MCS_CJCF = 15, /* Channel Join Confirm */
- MCS_SDRQ = 25, /* Send Data Request */
- MCS_SDIN = 26 /* Send Data Indication */
- };
protocal interface(协议接口):
- /* rdp.c */
- void rdp_out_unistr(STREAM s, char *string, int len);
- int rdp_in_unistr(STREAM s, char *string, int uni_len);
- void rdp_send_input(uint32 time, uint16 message_type, uint16 device_flags, uint16 param1,
- uint16 param2);
- void rdp_send_client_window_status(int status);
- void process_colour_pointer_pdu(STREAM s);
- void process_cached_pointer_pdu(STREAM s);
- void process_system_pointer_pdu(STREAM s);
- void process_bitmap_updates(STREAM s);
- void process_palette(STREAM s);
- void process_disconnect_pdu(STREAM s, uint32 * ext_disc_reason);
- void rdp_main_loop(BOOL * deactivated, uint32 * ext_disc_reason);
- BOOL rdp_loop(BOOL * deactivated, uint32 * ext_disc_reason);
- BOOL rdp_connect(char *server, uint32 flags, char *domain, char *password, char *command,
- char *directory);
- BOOL rdp_reconnect(char *server, uint32 flags, char *domain, char *password, char *command,
- char *directory, char *cookie);
- void rdp_reset_state(void);
- void rdp_disconnect(void);
- /* rdpdr.c */
- int get_device_index(RD_NTHANDLE handle);
- void convert_to_unix_filename(char *filename);
- BOOL rdpdr_init(void);
- void rdpdr_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv, BOOL * timeout);
- struct async_iorequest *rdpdr_remove_iorequest(struct async_iorequest *prev,
- struct async_iorequest *iorq);
- void rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out);
- BOOL rdpdr_abort_io(uint32 fd, uint32 major, RD_NTSTATUS status);
- /* rdpsnd.c */
- void rdpsnd_send_completion(uint16 tick, uint8 packet_index);
- BOOL rdpsnd_init(void);
- /* rdpsnd_oss.c */
- BOOL wave_out_open(void);
- void wave_out_close(void);
- BOOL wave_out_format_supported(WAVEFORMATEX * pwfx);
- BOOL wave_out_set_format(WAVEFORMATEX * pwfx);
- void wave_out_volume(uint16 left, uint16 right);
- void wave_out_write(STREAM s, uint16 tick, uint8 index);
- void wave_out_play(void);
- /* secure.c */
- void sec_hash_48(uint8 * out, uint8 * in, uint8 * salt1, uint8 * salt2, uint8 salt);
- void sec_hash_16(uint8 * out, uint8 * in, uint8 * salt1, uint8 * salt2);
- void buf_out_uint32(uint8 * buffer, uint32 value);
- void sec_sign(uint8 * signature, int siglen, uint8 * session_key, int keylen, uint8 * data,
- int datalen);
- void sec_decrypt(uint8 * data, int length);
- STREAM sec_init(uint32 flags, int maxlen);
- void sec_send_to_channel(STREAM s, uint32 flags, uint16 channel);
- void sec_send(STREAM s, uint32 flags);
- void sec_process_mcs_data(STREAM s);
- STREAM sec_recv(uint8 * rdpver);
- BOOL sec_connect(char *server, char *username);
- BOOL sec_reconnect(char *server);
- void sec_disconnect(void);
- void sec_reset_state(void);
- /* serial.c */
- int serial_enum_devices(uint32 * id, char *optarg);
- BOOL serial_get_event(RD_NTHANDLE handle, uint32 * result);
- BOOL serial_get_timeout(RD_NTHANDLE handle, uint32 length, uint32 * timeout, uint32 * itv_timeout);
- /* tcp.c */
- STREAM tcp_init(uint32 maxlen);
- void tcp_send(STREAM s);
- STREAM tcp_recv(STREAM s, uint32 length);
- BOOL tcp_connect(char *server);
- void tcp_disconnect(void);
- char *tcp_get_address(void);
- void tcp_reset_state(void);
ui interface(UI接口)
- /* xkeymap.c */
- BOOL xkeymap_from_locale(const char *locale);
- FILE *xkeymap_open(const char *filename);
- void xkeymap_init(void);
- BOOL handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, BOOL pressed);
- key_translation xkeymap_translate_key(uint32 keysym, unsigned int keycode, unsigned int state);
- void xkeymap_send_keys(uint32 keysym, unsigned int keycode, unsigned int state, uint32 ev_time,
- BOOL pressed, uint8 nesting);
- uint16 xkeymap_translate_button(unsigned int button);
- char *get_ksname(uint32 keysym);
- void save_remote_modifiers(uint8 scancode);
- void restore_remote_modifiers(uint32 ev_time, uint8 scancode);
- void ensure_remote_modifiers(uint32 ev_time, key_translation tr);
- unsigned int read_keyboard_state(void);
- uint16 ui_get_numlock_state(unsigned int state);
- void reset_modifier_keys(void);
- void rdp_send_scancode(uint32 time, uint16 flags, uint8 scancode);
- /* xwin.c */
- BOOL get_key_state(unsigned int state, uint32 keysym);
- BOOL ui_init(void);
- void ui_deinit(void);
- BOOL ui_create_window(void);
- void ui_resize_window(void);
- void ui_destroy_window(void);
- void xwin_toggle_fullscreen(void);
- int ui_select(int rdp_socket);
- void ui_move_pointer(int x, int y);
- RD_HBITMAP ui_create_bitmap(int width, int height, uint8 * data);
- void ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * data);
- void ui_destroy_bitmap(RD_HBITMAP bmp);
- RD_HGLYPH ui_create_glyph(int width, int height, uint8 * data);
- void ui_destroy_glyph(RD_HGLYPH glyph);
- RD_HCURSOR ui_create_cursor(unsigned int x, unsigned int y, int width, int height, uint8 * andmask,
- uint8 * xormask);
- void ui_set_cursor(RD_HCURSOR cursor);
- void ui_destroy_cursor(RD_HCURSOR cursor);
- void ui_set_null_cursor(void);
- RD_HCOLOURMAP ui_create_colourmap(COLOURMAP * colours);
- void ui_destroy_colourmap(RD_HCOLOURMAP map);
- void ui_set_colourmap(RD_HCOLOURMAP map);
- void ui_set_clip(int x, int y, int cx, int cy);
- void ui_reset_clip(void);
- void ui_bell(void);
- void ui_destblt(uint8 opcode, int x, int y, int cx, int cy);
- void ui_patblt(uint8 opcode, int x, int y, int cx, int cy, BRUSH * brush, int bgcolour,
- int fgcolour);
- void ui_screenblt(uint8 opcode, int x, int y, int cx, int cy, int srcx, int srcy);
- void ui_memblt(uint8 opcode, int x, int y, int cx, int cy, RD_HBITMAP src, int srcx, int srcy);
- void ui_triblt(uint8 opcode, int x, int y, int cx, int cy, RD_HBITMAP src, int srcx, int srcy,
- BRUSH * brush, int bgcolour, int fgcolour);
- void ui_line(uint8 opcode, int startx, int starty, int endx, int endy, PEN * pen);
- void ui_rect(int x, int y, int cx, int cy, int colour);
- void ui_polygon(uint8 opcode, uint8 fillmode, POINT * point, int npoints, BRUSH * brush,
- int bgcolour, int fgcolour);
- void ui_polyline(uint8 opcode, POINT * points, int npoints, PEN * pen);
- void ui_ellipse(uint8 opcode, uint8 fillmode, int x, int y, int cx, int cy, BRUSH * brush,
- int bgcolour, int fgcolour);
- void ui_draw_glyph(int mixmode, int x, int y, int cx, int cy, RD_HGLYPH glyph, int srcx, int srcy,
- int bgcolour, int fgcolour);
- void ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y, int clipx,
- int clipy, int clipcx, int clipcy, int boxx, int boxy, int boxcx, int boxcy,
- BRUSH * brush, int bgcolour, int fgcolour, uint8 * text, uint8 length);
- void ui_desktop_save(uint32 offset, int x, int y, int cx, int cy);
- void ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy);
- void ui_begin_update(void);
- void ui_end_update(void);
- void ui_seamless_begin(BOOL hidden);
- void ui_seamless_hide_desktop(void);
- void ui_seamless_unhide_desktop(void);
- void ui_seamless_toggle(void);
- void ui_seamless_create_window(unsigned long id, unsigned long group, unsigned long parent,
- unsigned long flags);
- void ui_seamless_destroy_window(unsigned long id, unsigned long flags);
- void ui_seamless_move_window(unsigned long id, int x, int y, int width, int height,
- unsigned long flags);
- void ui_seamless_restack_window(unsigned long id, unsigned long behind, unsigned long flags);
- void ui_seamless_settitle(unsigned long id, const char *title, unsigned long flags);
- void ui_seamless_setstate(unsigned long id, unsigned int state, unsigned long flags);
- void ui_seamless_syncbegin(unsigned long flags);
- void ui_seamless_ack(unsigned int serial);
其中,ui_select是整个UI的核心,负责UI调度。
cache interface(缓存接口)
- /* bitmap.c */
- BOOL bitmap_decompress(uint8 * output, int width, int height, uint8 * input, int size, int Bpp);
- /* cache.c */
- void cache_rebuild_bmpcache_linked_list(uint8 id, sint16 * idx, int count);
- void cache_bump_bitmap(uint8 id, uint16 idx, int bump);
- void cache_evict_bitmap(uint8 id);
- RD_HBITMAP cache_get_bitmap(uint8 id, uint16 idx);
- void cache_put_bitmap(uint8 id, uint16 idx, RD_HBITMAP bitmap);
- void cache_save_state(void);
- FONTGLYPH *cache_get_font(uint8 font, uint16 character);
- void cache_put_font(uint8 font, uint16 character, uint16 offset, uint16 baseline, uint16 width,
- uint16 height, RD_HGLYPH pixmap);
- DATABLOB *cache_get_text(uint8 cache_id);
- void cache_put_text(uint8 cache_id, void *data, int length);
- uint8 *cache_get_desktop(uint32 offset, int cx, int cy, int bytes_per_pixel);
- void cache_put_desktop(uint32 offset, int cx, int cy, int scanline, int bytes_per_pixel,
- uint8 * data);
- RD_HCURSOR cache_get_cursor(uint16 cache_idx);
- void cache_put_cursor(uint16 cache_idx, RD_HCURSOR cursor);
licence证书处理
- /* licence.c */
- void licence_process(STREAM s);
主进程函数
- /* rdesktop.c */
- int main(int argc, char *argv[]);
- void generate_random(uint8 * random);
- void *xmalloc(int size);
- char *xstrdup(const char *s);
- void *xrealloc(void *oldmem, int size);
- void xfree(void *mem);
- void error(char *format, ...);
- void warning(char *format, ...);
- void unimpl(char *format, ...);
- void hexdump(unsigned char *p, unsigned int len);
次序处理:
- /* orders.c */
- void process_orders(STREAM s, uint16 num_orders);
- void reset_order_state(void);
协议数据包:
- 128-bit encryption enabled
- Sending encrypted packet:
- 0000 00 00 00 00 33 00 00 00 00 00 1a 00 00 00 00 00 ....3...........
- 0010 00 00 00 00 41 00 64 00 6d 00 69 00 6e 00 69 00 ....A.d.m.i.n.i.
- 0020 73 00 74 00 72 00 61 00 74 00 6f 00 72 00 00 00 s.t.r.a.t.o.r...
- 0030 00 00 00 00 00 00 ......
- Connection successful.
- Sending encrypted packet:
- 0000 22 00 17 00 ec 03 00 00 00 00 00 01 14 00 1c 00 "...............
- 0010 00 00 01 00 00 00 11 4e 7a 4b 01 80 00 08 cf 01 .......NzK......
- 0020 91 00 ..
- Sending encrypted packet:
- 0000 22 00 17 00 ec 03 00 00 00 00 00 01 14 00 1c 00 "...............
- 0010 00 00 01 00 00 00 12 4e 7a 4b 01 80 00 08 cf 01 .......NzK......
- 0020 91 00 ..
- RDP packet (type 1):
- 0000 67 01 11 00 ea 03 ea 03 01 00 04 00 51 01 52 44 g...........Q.RD
- 0010 50 00 0d 00 00 00 09 00 08 00 ea 03 65 e3 01 00 P...........e...
- 0020 18 00 01 00 03 00 00 02 00 00 00 00 1d 04 00 00 ................
- 0030 00 00 00 00 01 01 14 00 08 00 02 00 00 00 16 00 ................
- 0040 28 00 01 00 00 00 6c 96 33 b7 01 00 00 00 95 a4 (.....l.3.......
- 0050 84 80 b0 7d 38 84 b8 5b c4 e1 f4 96 33 b7 ea e8 ...}8..[....3...
- 0060 84 80 20 02 c9 85 0e 00 04 00 02 00 1c 00 08 00 .. .............
- 0070 01 00 01 00 01 00 c0 03 e9 02 00 00 01 00 01 00 ................
- 0080 00 00 01 00 00 00 03 00 58 00 00 00 00 00 00 00 ........X.......
- 0090 00 00 00 00 00 00 00 00 00 00 40 42 0f 00 01 00 ..........@B....
- 00a0 14 00 00 00 01 00 00 00 22 00 01 01 01 01 01 00 ........".......
- 00b0 00 01 01 01 01 01 00 00 00 01 01 01 01 01 01 01 ................
- 00c0 01 00 01 01 01 01 00 00 00 00 a1 06 00 00 40 42 ..............@B
- 00d0 0f 00 40 42 0f 00 01 00 00 00 00 00 00 00 0a 00 ..@B............
- 00e0 08 00 06 00 00 00 12 00 08 00 01 00 00 00 08 00 ................
- 00f0 0a 00 01 00 19 00 19 00 0d 00 58 00 35 00 00 00 ..........X.5...
- 0100 a1 06 00 00 40 42 0f 00 0c 96 33 b7 75 7a 6f b7 ....@B....3.uzo.
- 0110 00 40 43 e1 48 3c 70 b7 40 96 33 b7 04 00 00 00 .@C.H<p.@.3.....
- 0120 4c 34 65 e3 08 30 65 e3 01 00 00 00 08 30 65 e3 L4e..0e......0e.
- 0130 00 00 00 00 38 96 33 b7 42 25 70 b7 08 30 65 e3 ....8.3.B%p..0e.
- 0140 2c 96 33 b7 00 00 00 00 08 00 0a 00 01 00 19 00 ,.3.............
- 0150 17 00 08 00 00 00 00 00 18 00 0b 00 00 00 00 00 ................
- 0160 00 00 00 00 00 00 00 .......
- DEMAND_ACTIVE(id=0x103ea)
- Sending encrypted packet:
- 0000 9a 01 13 00 ec 03 ea 03 01 00 ea 03 06 00 84 01 ................
- 0010 4d 53 54 53 43 00 0d 00 00 00 01 00 18 00 01 00 MSTSC...........
- 0020 03 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 0030 00 00 02 00 1c 00 08 00 01 00 01 00 01 00 20 03 .............. .
- 0040 58 02 00 00 00 00 01 00 00 00 01 00 00 00 03 00 X...............
- 0050 58 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 X...............
- 0060 00 00 00 00 00 00 01 00 14 00 00 00 01 00 47 01 ..............G.
- 0070 2a 00 01 01 01 01 00 00 00 00 01 01 01 01 00 01 *...............
- 0080 01 00 00 00 00 00 00 00 01 00 00 00 00 01 00 00 ................
- 0090 00 00 a1 06 00 00 00 00 00 00 00 84 03 00 00 00 ................
- 00a0 00 00 e4 04 00 00 04 00 28 00 00 00 00 00 00 00 ........(.......
- 00b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 00c0 00 00 58 02 00 01 2c 01 00 04 06 01 00 10 0a 00 ..X...,.........
- 00d0 08 00 06 00 00 00 07 00 0c 00 00 00 00 00 00 00 ................
- 00e0 00 00 05 00 0c 00 00 00 00 00 02 00 02 00 08 00 ................
- 00f0 08 00 00 00 14 00 09 00 08 00 00 00 00 00 0d 00 ................
- 0100 58 00 01 00 00 00 09 04 00 00 04 00 00 00 00 00 X...............
- 0110 00 00 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 0120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 0130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 0140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 0150 00 00 00 00 00 00 0c 00 08 00 01 00 00 00 0e 00 ................
- 0160 08 00 01 00 00 00 10 00 34 00 fe 00 04 00 fe 00 ........4.......
- 0170 04 00 fe 00 08 00 fe 00 08 00 fe 00 10 00 fe 00 ................
- 0180 20 00 fe 00 40 00 fe 00 80 00 fe 00 00 01 40 00 ...@.........@.
- 0190 00 08 00 01 00 01 02 00 00 00 ..........
- RDP packet (type 7):
- 0000 6d 00 17 00 ea 03 ea 03 01 00 3b 02 6d 00 02 00 m.........;.m...
- 0010 00 00 00 00 75 20 08 00 bb 3e 15 4a 01 50 21 44 ....u ...>.J.P!D
- 0020 21 23 01 00 00 15 4a 01 50 3f 40 3f 01 ff 01 00 !#....J.P?@?....
- 0030 15 02 01 50 40 40 40 02 00 15 02 01 50 40 40 40 ...P@@@.....P@@@
- 0040 03 00 15 02 01 50 40 40 40 01 00 15 0a 01 50 40 .....P@@@.....P@
- 0050 2f 40 ef 04 00 45 4a 05 12 01 22 01 12 01 11 00 /@...EJ...".....
- 0060 2f 00 15 4a 01 50 11 1c 11 0b d1 05 00 /..J.P.......
- MEMBLT(op=0xcc,x=319,y=297,cx=63,cy=5,id=1,idx=0)
- MEMBLT(op=0xcc,x=382,y=297,cx=64,cy=5,id=1,idx=1)
- MEMBLT(op=0xcc,x=446,y=297,cx=64,cy=5,id=1,idx=2)
- MEMBLT(op=0xcc,x=510,y=297,cx=64,cy=5,id=1,idx=3)
- MEMBLT(op=0xcc,x=574,y=297,cx=64,cy=5,id=1,idx=1)
- MEMBLT(op=0xcc,x=638,y=297,cx=47,cy=5,id=1,idx=4)
- MEMBLT(op=0xcc,x=274,y=297,cx=17,cy=5,id=1,idx=4)
- MEMBLT(op=0xcc,x=291,y=297,cx=28,cy=5,id=1,idx=5)
- RDP packet (type 7):
- 0000 1e 00 17 00 ea 03 ea 03 01 00 04 02 1e 00 02 00 ................
- 0010 00 00 00 00 85 7e 01 00 d5 f5 19 0b 20 01 .....~...... .
- DESKSAVE(l=271,t=200,r=687,b=343,off=0,op=1)
- RDP packet (type 6):
- 0000 0d 00 16 00 ea 03 ea 03 01 00 01 00 00 .............
- Disconnecting...