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
}
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;
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);
}
/*
- * 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);
/*
* 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.
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.
*/
int curtlv = 0;
int tlv1 = 0;
u_short curtype;
+ int lastvalid;
if (!buf || !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];
/*
/*
* 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 (z<len)
{
- 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 (z<len)
+ x = sprintf(tmpstr, "faim: userinfo: ");
+ for (y = 0; y < 8; y++)
{
- x = sprintf(tmpstr, "faim: userinfo: ");
- for (y = 0; y < 8; y++)
+ if (z<len)
{
- if (z<len)
- {
- sprintf(tmpstr+x, "%02x ", buf[i+4+z]);
- z++;
- x += 3;
- }
- else
- break;
+ sprintf(tmpstr+x, "%02x ", buf[i+4+z]);
+ z++;
+ x += 3;
}
- printf("%s\n", tmpstr);
+ else
+ break;
}
+ printf("%s\n", tmpstr);
}
- break;
- }
- /*
- * No matter what, TLV triplets should always look like this:
- *
- * u_short type;
- * u_short length;
- * u_char data[length];
- *
- */
- i += (2 + 2 + aimutil_get16(&buf[i+2]));
-
+ }
+ break;
+ }
+ /*
+ * No matter what, TLV triplets should always look like this:
+ *
+ * u_short type;
+ * u_short length;
+ * u_char data[length];
+ *
+ */
+ if (lastvalid) {
+ i += (2 + 2 + aimutil_get16(&buf[i+2]));
curtlv++;
}
+ }
return i;
}
*/
faim_mutex_lock(&conn->active);
if (read(conn->fd, generic, 6) < 6){
- aim_conn_close(conn);
+ aim_conn_kill(sess, &conn);
faim_mutex_unlock(&conn->active);
return -1;
}
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;
}
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;
+}
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);
* 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;
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) {
sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL));
}
- if (aim_tx_sendframe(cur) == -1)
+ if (aim_tx_sendframe(sess, cur) == -1)
break;
}
}
/*
* 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
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 *, ...);
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 *);
{
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
#endif
int selstat = 0;
- aim_session_init(&aimsess);
-
if ( !(screenname = getenv("SCREENNAME")) ||
!(password = getenv("PASSWORD")))
{
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) {
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 */
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;
/* 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);
}
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;
}