From 68ac63c2c144fc0151820d3c6feb1e3f78bba24b Mon Sep 17 00:00:00 2001 From: mid Date: Sat, 24 Jun 2000 02:01:31 +0000 Subject: [PATCH] - Sat Jun 24 00:44:24 UTC 2000 - Support zero-type TLVs in aim_extractinfo() - AOL now ocassionally throws extra zeros in so in order to break clients. Aparently zero-type TLVs are a special case -- if the type is zero, then _there is no length_ field. AOL are geniuses. - Note that you only get these if you send a bad client string, such as the top one in faimtest. If you #if 0 that one out and use the second one, you'll be fine. (Unless you just want to see the message for yourself. Its nifty. (FREE!)) - ICBM parser now uses aim_extractuserinfo() so that it can benefit from the above changes, as well as clean up the whole "is it the first or the second one of this type?" issue - This also fixes the changes AOL made in away messages. = Fixed a possible buffer overflow when AOL changes the max sn length - Increased MAXSNLEN to 32 (they appear to be valid) - If you start getting messages from someone called "AOL Instant Messenger", see comment one, paragraph two. - aim_select() now returns a -1 if there are no connections open - Connections are now killed completely if there is a major error on them - faimtest now calls aim_logoff() on connection error --- CHANGES | 21 +++ aim_conn.c | 6 +- aim_im.c | 96 +++--------- aim_info.c | 318 ++++++++++++++++++++------------------ aim_rxqueue.c | 22 ++- aim_txqueue.c | 12 +- faim/aim.h | 9 +- utils/faimtest/faimtest.c | 43 ++---- 8 files changed, 269 insertions(+), 258 deletions(-) diff --git a/CHANGES b/CHANGES index c9f7d28..9a47d4a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,27 @@ No release numbers ------------------ + - Sat Jun 24 00:44:24 UTC 2000 + - Support zero-type TLVs in aim_extractinfo() + - AOL now ocassionally throws extra zeros in so in order to break + clients. Aparently zero-type TLVs are a special case -- if the + type is zero, then _there is no length_ field. AOL are geniuses. + - Note that you only get these if you send a bad client string, such + as the top one in faimtest. If you #if 0 that one out and use the + second one, you'll be fine. (Unless you just want to see the message + for yourself. Its nifty. (FREE!)) + - ICBM parser now uses aim_extractuserinfo() so that it can benefit + from the above changes, as well as clean up the whole "is it the + first or the second one of this type?" issue + - This also fixes the changes AOL made in away messages. + = Fixed a possible buffer overflow when AOL changes the max sn length + - Increased MAXSNLEN to 32 (they appear to be valid) + - If you start getting messages from someone called "AOL Instant + Messenger", see comment one, paragraph two. + - aim_select() now returns a -1 if there are no connections open + - Connections are now killed completely if there is a major error on them + - faimtest now calls aim_logoff() on connection error + - Fri Jun 23 22:38:47 UTC 2000 - Adds start of adverts support - Adds aim_bos_nop(). You may want to send this occassionally diff --git a/aim_conn.c b/aim_conn.c index 061409f..2e1c35f 100644 --- a/aim_conn.c +++ b/aim_conn.c @@ -80,6 +80,9 @@ void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn) } faim_mutex_unlock(&sess->connlistlock); + /* XXX: do we need this for txqueue too? */ + aim_rxqueue_cleanbyconn(sess, *deadconn); + aim_conn_close(*deadconn); free(*deadconn); deadconn = NULL; @@ -255,7 +258,8 @@ struct aim_conn_t *aim_select(struct aim_session_t *sess, faim_mutex_lock(&sess->connlistlock); if (sess->connlist == NULL) { faim_mutex_unlock(&sess->connlistlock); - return 0; + *status = -1; + return NULL; } faim_mutex_unlock(&sess->connlistlock); diff --git a/aim_im.c b/aim_im.c index c380cc7..f82ecfa 100644 --- a/aim_im.c +++ b/aim_im.c @@ -276,27 +276,26 @@ int aim_parse_incoming_im_middle(struct aim_session_t *sess, } /* - * Source screen name. - */ - memcpy(userinfo.sn, command->data+i+1, (int)command->data[i]); - userinfo.sn[(int)command->data[i]] = '\0'; - i += 1 + (int)command->data[i]; - - /* - * Warning Level - */ - userinfo.warnlevel = aimutil_get16(command->data+i); /* guess */ - i += 2; - - /* - * Number of TLVs that follow. Not needed. + * Extract the standard user info block. + * + * Note that although this contains TLVs that appear contiguous + * with the TLVs read below, they are two different pieces. The + * userinfo block contains the number of TLVs that contain user + * information, the rest are not even though there is no seperation. + * aim_extractuserinfo() returns the number of bytes used by the + * userinfo tlvs, so you can start reading the rest of them right + * afterward. + * + * That also means that TLV types can be duplicated between the + * userinfo block and the rest of the message, however there should + * never be two TLVs of the same type in one block. + * */ - wastebits = aimutil_get16(command->data+i); - i += 2; + i += aim_extractuserinfo(command->data+i, &userinfo); /* - * Read block of TLVs. All further data is derived - * from what is parsed here. + * Read block of TLVs (not including the userinfo data). All + * further data is derived from what is parsed here. */ tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i); @@ -314,55 +313,26 @@ int aim_parse_incoming_im_middle(struct aim_session_t *sess, /* * Check Autoresponse status. If it is an autoresponse, - * it will contain a second type 0x0004 TLV, with zero length. + * it will contain a type 0x0004 TLV, with zero length. */ - if (aim_gettlv(tlvlist, 0x0004, 2)) + if (aim_gettlv(tlvlist, 0x0004, 1)) icbmflags |= AIM_IMFLAGS_AWAY; /* * Check Ack Request status. */ - if (aim_gettlv(tlvlist, 0x0003, 2)) + if (aim_gettlv(tlvlist, 0x0003, 1)) icbmflags |= AIM_IMFLAGS_ACK; - /* - * Extract the various pieces of the userinfo struct. - */ - /* Class. */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1))) - userinfo.class = aimutil_get16(tmptlv->value); - /* Member-since date. */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0002, 1))) - { - /* If this is larger than 4, its probably the message block, skip */ - if (tmptlv->length <= 4) - userinfo.membersince = aimutil_get32(tmptlv->value); - } - /* On-since date */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1))) - userinfo.onlinesince = aimutil_get32(tmptlv->value); - /* Idle-time */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1))) - userinfo.idletime = aimutil_get16(tmptlv->value); - /* Session Length (AIM) */ - if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1))) - userinfo.sessionlen = aimutil_get16(tmptlv->value); - /* Session Length (AOL) */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1))) - userinfo.sessionlen = aimutil_get16(tmptlv->value); - /* * Message block. - * - * XXX: Will the msgblock always be the second 0x0002? */ msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1); - if (!msgblocktlv || !msgblocktlv->value) - { - printf("faim: icbm: major error! no message block TLV found!\n"); - aim_freetlvchain(&tlvlist); - return 1; - } + if (!msgblocktlv || !msgblocktlv->value) { + printf("faim: icbm: major error! no message block TLV found!\n"); + aim_freetlvchain(&tlvlist); + return 1; + } /* * Extracting the message from the unknown cruft. @@ -438,22 +408,6 @@ int aim_parse_incoming_im_middle(struct aim_session_t *sess, unsigned short reqclass = 0; unsigned short status = 0; - /* Class. */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1))) - userinfo.class = aimutil_get16(tmptlv->value); - /* On-since date */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1))) - userinfo.onlinesince = aimutil_get32(tmptlv->value); - /* Idle-time */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1))) - userinfo.idletime = aimutil_get16(tmptlv->value); - /* Session Length (AIM) */ - if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1))) - userinfo.sessionlen = aimutil_get16(tmptlv->value); - /* Session Length (AOL) */ - if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1))) - userinfo.sessionlen = aimutil_get16(tmptlv->value); - /* * There's another block of TLVs embedded in the type 5 here. */ diff --git a/aim_info.c b/aim_info.c index 88f07fc..8015916 100644 --- a/aim_info.c +++ b/aim_info.c @@ -159,6 +159,7 @@ int aim_extractuserinfo(u_char *buf, struct aim_userinfo_s *outinfo) int curtlv = 0; int tlv1 = 0; u_short curtype; + int lastvalid; if (!buf || !outinfo) @@ -171,8 +172,13 @@ int aim_extractuserinfo(u_char *buf, struct aim_userinfo_s *outinfo) * Screen name. Stored as an unterminated string prepended * with an unsigned byte containing its length. */ - memcpy(outinfo->sn, &(buf[i+1]), buf[i]); - outinfo->sn[(int)buf[i]] = '\0'; + if (buf[i] < MAXSNLEN) { + memcpy(outinfo->sn, &(buf[i+1]), buf[i]); + outinfo->sn[(int)buf[i]] = '\0'; + } else { + memcpy(outinfo->sn, &(buf[i+1]), MAXSNLEN-1); + outinfo->sn[MAXSNLEN] = '\0'; + } i = 1 + (int)buf[i]; /* @@ -191,163 +197,177 @@ int aim_extractuserinfo(u_char *buf, struct aim_userinfo_s *outinfo) /* * Parse out the Type-Length-Value triples as they're found. */ - while (curtlv < tlvcnt) - { - curtype = aimutil_get16(&buf[i]); - switch (curtype) - { - /* - * Type = 0x0001: Member Class. - * - * Specified as any of the following bitwise ORed together: - * 0x0001 Trial (user less than 60days) - * 0x0002 Unknown bit 2 - * 0x0004 AOL Main Service user - * 0x0008 Unknown bit 4 - * 0x0010 Free (AIM) user - * 0x0020 Away - * - * In some odd cases, we can end up with more - * than one of these. We only want the first, - * as the others may not be something we want. - * - */ - case 0x0001: - if (tlv1) /* use only the first */ - break; - outinfo->class = aimutil_get16(&buf[i+4]); - tlv1++; - break; - - /* - * Type = 0x0002: Member-Since date. - * - * The time/date that the user originally - * registered for the service, stored in - * time_t format - */ - case 0x0002: - outinfo->membersince = aimutil_get32(&buf[i+4]); - break; - - /* - * Type = 0x0003: On-Since date. - * - * The time/date that the user started - * their current session, stored in time_t - * format. - */ - case 0x0003: - outinfo->onlinesince = aimutil_get32(&buf[i+4]); - break; - - /* - * Type = 0x0004: Idle time. - * - * Number of seconds since the user - * actively used the service. - */ - case 0x0004: - outinfo->idletime = aimutil_get16(&buf[i+4]); - break; - - /* - * Type = 0x000d - * - * Capability information. Not real sure of - * actual decoding. See comment on aim_bos_setprofile() - * in aim_misc.c about the capability block, its the same. - * - */ - case 0x000d: - { - int len; - len = aimutil_get16(buf+i+2); - if (!len) - break; - - outinfo->capabilities = aim_getcap(buf+i+4, len); - } - break; + while (curtlv < tlvcnt) { + lastvalid = 1; + curtype = aimutil_get16(&buf[i]); + switch (curtype) { + /* + * Type = 0x0000: Invalid + * + * AOL has been trying to throw these in just to break us. + * They're real nice guys over there at AOL. + * + * Just skip the two zero bytes and continue on. (This doesn't + * count towards tlvcnt!) + */ + case 0x0000: + lastvalid = 0; + i += 2; + break; - /* - * Type = 0x000e - * - * Unknown. Always of zero length, and always only - * on AOL users. - * - * Ignore. - * - */ - case 0x000e: - break; - - /* - * Type = 0x000f: Session Length. (AIM) - * Type = 0x0010: Session Length. (AOL) - * - * The duration, in seconds, of the user's - * current session. - * - * Which TLV type this comes in depends - * on the service the user is using (AIM or AOL). - * - */ - case 0x000f: - case 0x0010: - outinfo->sessionlen = aimutil_get32(&buf[i+4]); + /* + * Type = 0x0001: Member Class. + * + * Specified as any of the following bitwise ORed together: + * 0x0001 Trial (user less than 60days) + * 0x0002 Unknown bit 2 + * 0x0004 AOL Main Service user + * 0x0008 Unknown bit 4 + * 0x0010 Free (AIM) user + * 0x0020 Away + * + * In some odd cases, we can end up with more + * than one of these. We only want the first, + * as the others may not be something we want. + * + */ + case 0x0001: + if (tlv1) /* use only the first */ + break; + outinfo->class = aimutil_get16(&buf[i+4]); + tlv1++; + break; + + /* + * Type = 0x0002: Member-Since date. + * + * The time/date that the user originally + * registered for the service, stored in + * time_t format + */ + case 0x0002: + outinfo->membersince = aimutil_get32(&buf[i+4]); + break; + + /* + * Type = 0x0003: On-Since date. + * + * The time/date that the user started + * their current session, stored in time_t + * format. + */ + case 0x0003: + outinfo->onlinesince = aimutil_get32(&buf[i+4]); + break; + + /* + * Type = 0x0004: Idle time. + * + * Number of seconds since the user + * actively used the service. + */ + case 0x0004: + outinfo->idletime = aimutil_get16(&buf[i+4]); + break; + + /* + * Type = 0x000d + * + * Capability information. Not real sure of + * actual decoding. See comment on aim_bos_setprofile() + * in aim_misc.c about the capability block, its the same. + * + */ + case 0x000d: + { + int len; + len = aimutil_get16(buf+i+2); + if (!len) break; - - /* - * Reaching here indicates that either AOL has - * added yet another TLV for us to deal with, - * or the parsing has gone Terribly Wrong. - * - * Either way, inform the owner and attempt - * recovery. - * - */ - default: + + outinfo->capabilities = aim_getcap(buf+i+4, len); + } + break; + + /* + * Type = 0x000e + * + * Unknown. Always of zero length, and always only + * on AOL users. + * + * Ignore. + * + */ + case 0x000e: + break; + + /* + * Type = 0x000f: Session Length. (AIM) + * Type = 0x0010: Session Length. (AOL) + * + * The duration, in seconds, of the user's + * current session. + * + * Which TLV type this comes in depends + * on the service the user is using (AIM or AOL). + * + */ + case 0x000f: + case 0x0010: + outinfo->sessionlen = aimutil_get32(&buf[i+4]); + break; + + /* + * Reaching here indicates that either AOL has + * added yet another TLV for us to deal with, + * or the parsing has gone Terribly Wrong. + * + * Either way, inform the owner and attempt + * recovery. + * + */ + default: + { + int len,z = 0, y = 0, x = 0; + char tmpstr[80]; + printf("faim: userinfo: **warning: unexpected TLV:\n"); + printf("faim: userinfo: sn =%s\n", outinfo->sn); + printf("faim: userinfo: curtlv=0x%04x\n", curtlv); + printf("faim: userinfo: type =0x%04x\n",aimutil_get16(&buf[i])); + printf("faim: userinfo: length=0x%04x\n", len = aimutil_get16(&buf[i+2])); + printf("faim: userinfo: data: \n"); + while (zsn); - printf("faim: userinfo: curtlv=0x%04x\n", curtlv); - printf("faim: userinfo: type =0x%04x\n",aimutil_get16(&buf[i])); - printf("faim: userinfo: length=0x%04x\n", len = aimutil_get16(&buf[i+2])); - printf("faim: userinfo: data: \n"); - while (zactive); if (read(conn->fd, generic, 6) < 6){ - aim_conn_close(conn); + aim_conn_kill(sess, &conn); faim_mutex_unlock(&conn->active); return -1; } @@ -89,7 +89,7 @@ int aim_get_command(struct aim_session_t *sess, struct aim_conn_t *conn) if (read(conn->fd, newrx->data, newrx->commandlen) < newrx->commandlen){ free(newrx->data); free(newrx); - aim_conn_close(conn); + aim_conn_kill(sess, &conn); faim_mutex_unlock(&conn->active); return -1; } @@ -259,3 +259,21 @@ void aim_purge_rxqueue(struct aim_session_t *sess) return; } + +/* + * Since aim_get_command will aim_conn_kill dead connections, we need + * to clean up the rxqueue of unprocessed connections on that socket. + * + * XXX: this is something that was handled better in the old connection + * handling method, but eh. + */ +void aim_rxqueue_cleanbyconn(struct aim_session_t *sess, struct aim_conn_t *conn) +{ + struct command_rx_struct *currx; + + for (currx = sess->queue_incoming; currx; currx = currx->next) { + if ((!currx->handled) && (currx->conn == conn)) + currx->handled = 1; + } + return; +} diff --git a/aim_txqueue.c b/aim_txqueue.c index 619137d..7c2da33 100644 --- a/aim_txqueue.c +++ b/aim_txqueue.c @@ -138,7 +138,7 @@ int aim_tx_enqueue__immediate(struct aim_session_t *sess, struct command_tx_stru newpacket->lock = 1; /* lock */ newpacket->sent = 0; /* not sent yet */ - aim_tx_sendframe(newpacket); + aim_tx_sendframe(sess, newpacket); if (newpacket->data) free(newpacket->data); @@ -225,7 +225,7 @@ int aim_tx_printqueue(struct aim_session_t *sess) * 9) Step to next struct in list and go back to 1. * */ -int aim_tx_sendframe(struct command_tx_struct *cur) +int aim_tx_sendframe(struct aim_session_t *sess, struct command_tx_struct *cur) { int buflen = 0; unsigned char *curPacket; @@ -288,9 +288,9 @@ int aim_tx_sendframe(struct command_tx_struct *cur) faim_mutex_lock(&cur->conn->active); if ( (u_int)write(cur->conn->fd, curPacket, buflen) != buflen) { faim_mutex_unlock(&cur->conn->active); - printf("\nWARNING: Error in sending packet -- will try again next time\n\n"); - cur->sent = 0; /* mark it unsent */ - return 0; /* bail out -- continuable error */ + cur->sent = 1; + aim_conn_kill(sess, &cur->conn); + return 0; /* bail out */ } if ((cur->hdrtype == AIM_FRAMETYPE_OFT) && cur->commandlen) { @@ -352,7 +352,7 @@ int aim_tx_flushqueue(struct aim_session_t *sess) sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL)); } - if (aim_tx_sendframe(cur) == -1) + if (aim_tx_sendframe(sess, cur) == -1) break; } } diff --git a/faim/aim.h b/faim/aim.h index 331f9f1..855a778 100644 --- a/faim/aim.h +++ b/faim/aim.h @@ -58,8 +58,11 @@ /* * Current Maximum Length for Screen Names (not including NULL) + * + * Currently only names up to 16 characters can be registered + * however it is aparently legal for them to be larger. */ -#define MAXSNLEN 16 +#define MAXSNLEN 32 /* * Current Maximum Length for Instant Messages @@ -370,7 +373,7 @@ unsigned long aim_sendredirect(struct aim_session_t *sess, char *ip, char *cookie); void aim_purge_rxqueue(struct aim_session_t *); - +void aim_rxqueue_cleanbyconn(struct aim_session_t *sess, struct aim_conn_t *conn); int aim_parse_unknown(struct aim_session_t *, struct command_rx_struct *command, ...); int aim_parse_missed_im(struct aim_session_t *, struct command_rx_struct *, ...); @@ -381,7 +384,7 @@ struct command_tx_struct *aim_tx_new(unsigned short framing, int chan, struct ai int aim_tx_enqueue__queuebased(struct aim_session_t *, struct command_tx_struct *); int aim_tx_enqueue__immediate(struct aim_session_t *, struct command_tx_struct *); #define aim_tx_enqueue(x, y) ((*(x->tx_enqueue))(x, y)) -int aim_tx_sendframe(struct command_tx_struct *cur); +int aim_tx_sendframe(struct aim_session_t *sess, struct command_tx_struct *cur); u_int aim_get_next_txseqnum(struct aim_conn_t *); int aim_tx_flushqueue(struct aim_session_t *); int aim_tx_printqueue(struct aim_session_t *); diff --git a/utils/faimtest/faimtest.c b/utils/faimtest/faimtest.c index 8dac713..4763eda 100644 --- a/utils/faimtest/faimtest.c +++ b/utils/faimtest/faimtest.c @@ -81,9 +81,9 @@ int main(void) { struct aim_session_t aimsess; struct aim_conn_t *authconn = NULL, *waitingconn = NULL; - int keepgoing = 1, stayconnected = 1; + int keepgoing = 1; -#if 1 +#if 0 /* Use something like this for AIM */ struct client_info_s info = {"Boo", 2, 1, 0, "us", "en"}; #else @@ -92,8 +92,6 @@ int main(void) #endif int selstat = 0; - aim_session_init(&aimsess); - if ( !(screenname = getenv("SCREENNAME")) || !(password = getenv("PASSWORD"))) { @@ -103,11 +101,8 @@ int main(void) server = getenv("AUTHSERVER"); - /* - * (I used a goto-based loop here because n wanted quick proof - * that reconnecting without restarting was actually possible...) - */ - enter: + aim_session_init(&aimsess); + authconn = aim_newconn(&aimsess, AIM_CONN_TYPE_AUTH, server?server:FAIM_LOGIN_SERVER); if (authconn == NULL) { @@ -120,27 +115,27 @@ int main(void) fprintf(stderr, "faimtest: could not connect to authorizer\n"); aim_conn_kill(&aimsess, &authconn); return -1; - } else { + } #ifdef SNACLOGIN - /* new login code -- not default -- pending new password encryption algo */ - aim_conn_addhandler(&aimsess, authconn, 0x0017, 0x0007, faimtest_parse_login, 0); - aim_conn_addhandler(&aimsess, authconn, 0x0017, 0x0003, faimtest_parse_authresp, 0); + /* new login code -- not default -- pending new password encryption algo */ + aim_conn_addhandler(&aimsess, authconn, 0x0017, 0x0007, faimtest_parse_login, 0); + aim_conn_addhandler(&aimsess, authconn, 0x0017, 0x0003, faimtest_parse_authresp, 0); - aim_sendconnack(&aimsess, authconn); - aim_request_login(&aimsess, authconn, FAIMTEST_SCREENNAME); + aim_sendconnack(&aimsess, authconn); + aim_request_login(&aimsess, authconn, FAIMTEST_SCREENNAME); #else - aim_conn_addhandler(&aimsess, authconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_AUTHSUCCESS, faimtest_parse_authresp, 0); - aim_conn_addhandler(&aimsess, authconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, faimtest_authsvrready, 0); - aim_send_login(&aimsess, authconn, screenname, password, &info); + aim_conn_addhandler(&aimsess, authconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_AUTHSUCCESS, faimtest_parse_authresp, 0); + aim_conn_addhandler(&aimsess, authconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, faimtest_authsvrready, 0); + aim_send_login(&aimsess, authconn, screenname, password, &info); #endif - } + printf("faimtest: login sent\n"); while (keepgoing) { waitingconn = aim_select(&aimsess, NULL, &selstat); switch(selstat) { case -1: /* error */ - keepgoing = 0; + keepgoing = 0; /* fall through and hit the aim_logoff() */ break; case 0: /* no events pending */ @@ -153,6 +148,7 @@ int main(void) case 2: /* incoming data pending */ if (aim_get_command(&aimsess, waitingconn) < 0) { printf("\afaimtest: connection error!\n"); + keepgoing = 0; /* fall through and hit the aim_logoff() */ } else aim_rxdispatch(&aimsess); break; @@ -168,12 +164,6 @@ int main(void) /* close up all connections, dead or no */ aim_logoff(&aimsess); - if (stayconnected) { - printf("\nTrying to reconnect in 2 seconds...\n"); - sleep(2); - goto enter; - } - /* Get out */ exit(0); } @@ -979,6 +969,7 @@ int faimtest_parse_connerr(struct aim_session_t *sess, struct command_rx_struct va_end(ap); printf("faimtest: connerr: Code 0x%04x: %s\n", code, msg); + aim_conn_kill(sess, &command->conn); /* this will break the main loop */ return 1; } -- 2.45.1