/** _ ** (_)_ __ __ ___ _ __ _ __ ** | | ' \/ _/ _ \ ' \| ' \ ** |_|_|_|_\__\___/_|_|_|_|_|_| ** ** Copyright (C) 2003-2005, Claudio Leite ** All rights reserved. ** ** Please see the file 'COPYING' for licensing information. **/ #include "imcomm.h" #ifdef MD5_LOGIN #include "md5.h" #endif #if 0 #define CLIENT_IDENT "AOL Instant Messenger, version 5.5.3595/WIN32" #define CLIENT_V1 0x0109 #define CLIENT_V2 0x0005 #define CLIENT_V3 0x0005 #define CLIENT_V4 0x0000 #define CLIENT_V5 0x0e0b #define CLIENT_V6 0x00000104 #else #define CLIENT_IDENT "Apple iChat" #define CLIENT_V1 0x311a #define CLIENT_V2 0x0001 #define CLIENT_V3 0x0000 #define CLIENT_V4 0x0000 #define CLIENT_V5 0x003c #define CLIENT_V6 0x00000c6 #endif #ifdef MACINTOSH_CLASSIC /* * Thanks to PuTTY source code for a lot of help on the MacTCP stuff. * Learning obsolete API's can be rather difficult... * * This should explain the similarities on variable and function names. */ int initialized = 0; short refnum; ProcessSerialNumber psn; static pascal void mactcp_lookupdone(struct hostInfo *, char *); static pascal void mactcp_asr(StreamPtr, unsigned short, Ptr, unsigned short, struct ICMPReport *); static ResultUPP mactcp_lookupdone_upp; static TCPNotifyUPP mactcp_asr_upp; OSErr err; static pascal void mactcp_lookupdone(struct hostInfo * hi, char *cookie) { volatile int *donep = (int *) cookie; *donep = TRUE; } static pascal void mactcp_asr(StreamPtr str, unsigned short event, Ptr cookie, unsigned short termin_reason, struct ICMPReport * icmp) { WakeUpProcess(&psn); } void mactcp_close(void *handle) { TCPiopb pb; pb.ioCRefNum = refnum; pb.csCode = TCPClose; pb.tcpStream = ((IMCOMM *) handle)->s; pb.csParam.close.validityFlags = 0; pb.csParam.close.userDataPtr = (Ptr) handle; PBControlSync((ParmBlkPtr) & pb); pb.ioCRefNum = refnum; pb.csCode = TCPAbort; pb.tcpStream = ((IMCOMM *) handle)->s; pb.csParam.abort.userDataPtr = (Ptr) handle; PBControlSync((ParmBlkPtr) & pb); pb.ioCRefNum = refnum; pb.csCode = TCPRelease; pb.tcpStream = ((IMCOMM *) handle)->s; pb.csParam.create.userDataPtr = (Ptr) handle; err = PBControlSync((ParmBlkPtr) & pb); if (err == noErr) free(pb.csParam.create.rcvBuff); } #endif /* PROTO */ IMCOMM_RET imcomm_im_signon(void *handle, const char *sn, const char *pw) { IMCOMM_RET ret; pkt_t *packet; #ifdef MACINTOSH_CLASSIC OSErr err; struct hostInfo hostinfo; volatile int resolverDone = FALSE; TCPiopb pb; UDPiopb upb; size_t buflen; #else struct sockaddr_in sin; struct hostent *he; #endif #ifndef MD5_LOGIN const char client_ident[] = CLIENT_IDENT; const unsigned char roaststring[] = {0xF3, 0x26, 0x81, 0xC4, 0x39, 0x86, 0xDB, 0x92, 0x71, 0xA3, 0xB9, 0xE6, 0x53, 0x7A, 0x95, 0x7C}; unsigned char *roastpw; int x; #else pkt_t *pk2; #endif #ifdef MACINTOSH_CLASSIC if (!initialized) { err = OpenDriver("\p.IPP", &refnum); if (err != noErr) { printf("Error initializing driver.\n"); return IMCOMM_RET_ERROR; } err = OpenResolver(NULL); if (err != noErr) { printf("Error initializing resolver.\n"); return IMCOMM_RET_ERROR; } mactcp_lookupdone_upp = NewResultProc(&mactcp_lookupdone); mactcp_asr_upp = NewTCPNotifyProc(&mactcp_asr); initialized = 1; } err = StrToAddr("login.oscar.aol.com", &hostinfo, mactcp_lookupdone_upp, (char *) &resolverDone); if (err == cacheFault) while (!resolverDone) continue; upb.ioCRefNum = refnum; upb.csCode = UDPMaxMTUSize; upb.csParam.mtu.remoteHost = hostinfo.addr[0]; upb.csParam.mtu.userDataPtr = (Ptr) handle; err = PBControlSync((ParmBlkPtr) & upb); if (err != noErr) { printf("Error retrieving MTU.\n"); return IMCOMM_RET_ERROR; } buflen = upb.csParam.mtu.mtuSize * 4 + 1024; if (buflen < 4096) buflen = 4096; GetCurrentProcess(&psn); pb.ioCRefNum = refnum; pb.csCode = TCPCreate; pb.csParam.create.rcvBuff = malloc(buflen); pb.csParam.create.rcvBuffLen = buflen; pb.csParam.create.notifyProc = mactcp_asr_upp; pb.csParam.create.userDataPtr = (Ptr) handle; err = PBControlSync((ParmBlkPtr) & pb); if (err != noErr) { printf("Error creating TCP stream.\n"); return IMCOMM_RET_ERROR; } ((IMCOMM *) handle)->s = pb.tcpStream; pb.ioCRefNum = refnum; pb.csCode = TCPActiveOpen; pb.tcpStream = ((IMCOMM *) handle)->s; pb.csParam.open.validityFlags = 0; pb.csParam.open.remoteHost = hostinfo.addr[0]; pb.csParam.open.remotePort = ((IMCOMM *) handle)->oscarport; pb.csParam.open.localPort = 0; pb.csParam.open.timeToLive = 0; pb.csParam.open.security = 0; pb.csParam.open.optionCnt = 0; pb.csParam.open.userDataPtr = (Ptr) handle; err = PBControlSync((ParmBlkPtr) & pb); if (err != noErr) { printf("Connection failed.\n"); return IMCOMM_RET_ERROR; } ((IMCOMM *) handle)->readable = 1; #else /* XXX insert modular socket code here */ #ifdef __MINGW32__ if (WSAStartup(0x101, &((IMCOMM *) handle)->wsadata)) { printf("ERROR: %i\n", WSAGetLastError()); return IMCOMM_RET_ERROR; } #endif if (((IMCOMM *) handle)->proxymode == PROXY_TYPE_SOCKS5) { connect_socks5(handle, "login.oscar.aol.com", ((IMCOMM *) handle)->oscarport); } else if (((IMCOMM *) handle)->proxymode == PROXY_TYPE_HTTPS) { connect_https(handle, "login.oscar.aol.com", ((IMCOMM *) handle)->oscarport); } else { if ((he = gethostbyname("login.oscar.aol.com")) == NULL) { perror("gethostbyname()"); return IMCOMM_RET_ERROR; } if ((((IMCOMM *) handle)->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket()"); return IMCOMM_RET_ERROR; } sin.sin_family = AF_INET; sin.sin_port = htons(((IMCOMM *) handle)->oscarport); sin.sin_addr = *((struct in_addr *) he->h_addr); memset(&(sin.sin_zero), 0, 8); if (connect (((IMCOMM *) handle)->socket, (struct sockaddr *) & sin, sizeof(struct sockaddr)) == -1) { perror("connect()"); return IMCOMM_RET_ERROR; } } /* end socket code for now */ #endif #ifdef MD5_LOGIN ((IMCOMM *) handle)->pw = strdup((char *) pw); ((IMCOMM *) handle)->sn = strdup((char *) sn); packet = pkt_init(12 + strlen(sn)); #else roastpw = malloc(strlen(pw) + 1); memset(roastpw, 0, strlen(pw) + 1); for (x = 0; x < (int) strlen(pw); x++) roastpw[x] = (pw[x] ^ roaststring[x]); packet = pkt_init(66 + strlen(client_ident) + strlen(sn) + strlen(pw)); pkt_add32(packet, 0x00000001); #endif /* * I should really build a TLV here instead, but this works. */ /* * Add SN length + SN */ pkt_add16(packet, 0x0001); pkt_add16(packet, (uint16_t) strlen(sn)); pkt_addraw(packet, (uint8_t *) sn, strlen(sn)); #ifdef MD5_LOGIN /* * Add unknown TLV's... */ pkt_add16(packet, 0x004B); pkt_add16(packet, 0x0000); pkt_add16(packet, 0x005A); pkt_add16(packet, 0x0000); #else /* * Add roasted PW length + roasted PW */ pkt_add16(packet, 0x0002); pkt_add16(packet, (uint16_t) strlen(pw)); pkt_addraw(packet, (uint8_t *) pw, strlen(pw)); /* * Add client ident string */ pkt_add16(packet, 0x0003); pkt_add16(packet, (uint16_t) strlen(client_ident)); pkt_addraw(packet, (uint8_t *) client_ident, strlen(client_ident)); /* * Add client ID (hardcoded) */ pkt_add16(packet, 0x0016); pkt_add16(packet, 0x0002); pkt_add16(packet, 0x0109); /* * Add client versions (hardcoded) */ pkt_add16(packet, 0x0017); pkt_add16(packet, 0x0002); pkt_add16(packet, 0x0005); pkt_add16(packet, 0x0018); pkt_add16(packet, 0x0002); pkt_add16(packet, 0x0005); pkt_add16(packet, 0x0019); pkt_add16(packet, 0x0002); pkt_add16(packet, 0x0000); pkt_add16(packet, 0x001A); pkt_add16(packet, 0x0002); pkt_add16(packet, 0x0057); pkt_add16(packet, 0x0014); pkt_add16(packet, 0x0004); pkt_add32(packet, 0x000000ef); pkt_add16(packet, 0x000F); pkt_add16(packet, 0x0002); pkt_addraw(packet, (unsigned char *) "en", 2); pkt_add16(packet, 0x000E); pkt_add16(packet, 0x0002); pkt_addraw(packet, (unsigned char *) "us", 2); #endif #ifdef MD5_LOGIN pk2 = pkt_init(4); pkt_add32(pk2, 0x00000001); flap_sendpkt(handle, 0x01, pk2, 0); pkt_free(pk2); ret = snac_sendpkt(handle, 0x17, 0x06, packet, 0); #else ret = flap_sendpkt(handle, 0x01, packet, 0); free(roastpw); #endif pkt_free(packet); return ret; } /* PROTO */ IMCOMM_RET bos_signon_phase2(void *handle, unsigned const char *server, unsigned const char *cookie, uint16_t cookie_len) { char *host; uint16_t port; pkt_t *packet; int x, ret; int newlen; #ifdef MACINTOSH_CLASSIC OSErr err; struct hostInfo hostinfo; volatile int resolverDone = FALSE; TCPiopb pb; UDPiopb upb; size_t buflen; #else struct sockaddr_in sin; #endif #if defined(PLAN9) || defined(__MINGW32__) long addy; struct in_addr ina; #else struct hostent *he; #endif port = atoi(strchr((char *) server, ':') + 1); host = (char *) malloc(strlen((char *) server) + 1); #ifdef __OpenBSD__ strlcpy(host, (char *) server, strlen((char *) server)); #else strcpy(host, (char *) server); #endif for (x = 0; x < (int) strlen(host); x++) if (host[x] == ':') { host[x] = 0; break; } #ifdef MACINTOSH_CLASSIC mactcp_close(handle); ((IMCOMM *) handle)->readable = 0; err = StrToAddr(host, &hostinfo, mactcp_lookupdone_upp, (char *) &resolverDone); if (err == cacheFault) while (!resolverDone) continue; upb.ioCRefNum = refnum; upb.csCode = UDPMaxMTUSize; upb.csParam.mtu.remoteHost = hostinfo.addr[0]; upb.csParam.mtu.userDataPtr = (Ptr) handle; err = PBControlSync((ParmBlkPtr) & upb); if (err != noErr) { printf("Error retrieving MTU.\n"); return IMCOMM_RET_ERROR; } buflen = upb.csParam.mtu.mtuSize * 4 + 1024; if (buflen < 4096) buflen = 4096; pb.ioCRefNum = refnum; pb.csCode = TCPCreate; pb.csParam.create.rcvBuff = malloc(buflen); pb.csParam.create.rcvBuffLen = buflen; pb.csParam.create.notifyProc = mactcp_asr_upp; pb.csParam.create.userDataPtr = (Ptr) handle; err = PBControlSync((ParmBlkPtr) & pb); if (err != noErr) { printf("Error creating TCP stream.\n"); return IMCOMM_RET_ERROR; } ((IMCOMM *) handle)->s = pb.tcpStream; pb.ioCRefNum = refnum; pb.csCode = TCPActiveOpen; pb.tcpStream = ((IMCOMM *) handle)->s; pb.csParam.open.validityFlags = 0; pb.csParam.open.remoteHost = hostinfo.addr[0]; pb.csParam.open.remotePort = port; pb.csParam.open.localPort = 0; pb.csParam.open.timeToLive = 0; pb.csParam.open.security = 0; pb.csParam.open.optionCnt = 0; pb.csParam.open.userDataPtr = (Ptr) handle; err = PBControlSync((ParmBlkPtr) & pb); if (err != noErr) { printf("Connection failed.\n"); return IMCOMM_RET_ERROR; } ((IMCOMM *) handle)->readable = 1; #else if (((IMCOMM *) handle)->ischild == 0) shutdown(((IMCOMM *) handle)->socket, 0x02); if (((IMCOMM *) handle)->proxymode == PROXY_TYPE_SOCKS5) { connect_socks5(handle, host, port); } else if (((IMCOMM *) handle)->proxymode == PROXY_TYPE_HTTPS) { connect_https(handle, host, port); } else { #if !defined(PLAN9) && !defined(__MINGW32__) if ((he = gethostbyname(host)) == NULL) { perror("gethostbyname()"); free(host); return IMCOMM_RET_ERROR; } free(host); #else addy = inet_addr(host); ina.s_addr = addy; free(host); #endif if ((((IMCOMM *) handle)->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket()"); return IMCOMM_RET_ERROR; } sin.sin_family = AF_INET; sin.sin_port = htons(port); #if defined(PLAN9) || defined(__MINGW32__) sin.sin_addr = ina; #else sin.sin_addr = *((struct in_addr *) he->h_addr); #endif memset(&(sin.sin_zero), 0, 8); if (connect (((IMCOMM *) handle)->socket, (struct sockaddr *) & sin, sizeof(struct sockaddr)) == -1) { perror("connect()"); return IMCOMM_RET_ERROR; } #endif } newlen = cookie_len + 8; packet = pkt_init(newlen); pkt_add32(packet, 0x00000001); pkt_add16(packet, 0x0006); pkt_add16(packet, cookie_len); pkt_addraw(packet, (uint8_t *) cookie, cookie_len); ret = flap_sendpkt(handle, 0x01, packet, 0); pkt_free(packet); return ret; } /* PROTO */ void handle_srv_migration(void *handle, uint8_t * data, uint16_t len) { TLVLIST *tlv, *trav; uint16_t num_family, cookie_len = 0; pkt_t *inpkt; int status = 0; char *bos_server = 0; uint8_t *cookie = 0; inpkt = pkt_initP(data + 10, len - 10); num_family = pkt_get16(inpkt); if (num_family != 0 && num_family != ((IMCOMM *) handle)->num_families) { printf("migration has different number of families.\n"); printf("not proceeding (bifurcated?)\n"); return; } pkt_freeP(inpkt); if (((IMCOMM *) handle)->srv_pause) ((IMCOMM *) handle)->srv_pause = 0; tlv = tlv_split(data + 12 + (num_family * 2), len - (12 + (num_family * 2)), 0); printf("migration beginning.\n"); for (trav = tlv; trav != NULL; trav = trav->next) { if (trav->type == 0x0005) { bos_server = malloc(trav->len + 1); memcpy(bos_server, trav->value, trav->len); bos_server[trav->len] = 0; status++; } else if (trav->type == 0x0006) { cookie = malloc(trav->len); cookie_len = trav->len; memcpy(cookie, trav->value, trav->len); status++; } } clear_tlv_list(tlv); printf("server: %s\n", bos_server); if (((IMCOMM *) handle)->callbacks[IMCOMM_ERROR]) ((IMCOMM *) handle)->callbacks[IMCOMM_ERROR] (handle, IMCOMM_STATUS_MIGRATIONDONE); bos_signon_phase2(handle, (unsigned char *) bos_server, cookie, cookie_len); free(bos_server); free(cookie); } #ifdef MD5_LOGIN #define AIM_MD5_STRING "AOL Instant Messenger (SM)" /* * New stuff for MD5-based login */ /* PROTO */ void bos_md5snac(void *handle, uint8_t * data, uint16_t len) { uint8_t *authkey; uint16_t authkeylen; md5_state_t state; md5_byte_t auth_hash[16]; pkt_t *packet; if (data[3] == 0x07) { const char client_ident[] = CLIENT_IDENT; authkeylen = two_to_16(data + 10); authkey = malloc(authkeylen); memcpy(authkey, data + 12, authkeylen); md5_init(&state); md5_append(&state, (const md5_byte_t *) authkey, authkeylen); md5_append(&state, (const md5_byte_t *) ((IMCOMM *) handle)->pw, strlen(((IMCOMM *) handle)->pw)); md5_append(&state, (const md5_byte_t *) AIM_MD5_STRING, strlen(AIM_MD5_STRING)); md5_finish(&state, auth_hash); free(authkey); packet = pkt_init(67 + strlen(CLIENT_IDENT) + 16 + strlen(((IMCOMM *) handle)->sn)); /* * I should really build a TLV here instead, but this works. */ /* * Add SN length + SN */ pkt_add16(packet, 0x0001); pkt_add16(packet, (uint16_t) strlen(((IMCOMM *) handle)->sn)); pkt_addraw(packet, (uint8_t *) ((IMCOMM *) handle)->sn, strlen(((IMCOMM *) handle)->sn)); pkt_add16(packet, 0x0025); pkt_add16(packet, 0x0010); pkt_addraw(packet, (uint8_t *) auth_hash, 16); /* * Add client ident string */ pkt_add16(packet, 0x0003); pkt_add16(packet, (uint16_t) strlen(client_ident)); pkt_addraw(packet, (uint8_t *) client_ident, strlen(client_ident)); /* * Add client ID (hardcoded) */ pkt_add16(packet, 0x0016); pkt_add16(packet, 0x0002); pkt_add16(packet, CLIENT_V1); /* * Add client versions (hardcoded) */ pkt_add16(packet, 0x0017); pkt_add16(packet, 0x0002); pkt_add16(packet, CLIENT_V2); pkt_add16(packet, 0x0018); pkt_add16(packet, 0x0002); pkt_add16(packet, CLIENT_V3); pkt_add16(packet, 0x0019); pkt_add16(packet, 0x0002); pkt_add16(packet, CLIENT_V4); pkt_add16(packet, 0x001A); pkt_add16(packet, 0x0002); pkt_add16(packet, CLIENT_V5); pkt_add16(packet, 0x0014); pkt_add16(packet, 0x0004); pkt_add32(packet, CLIENT_V6); pkt_add16(packet, 0x000F); pkt_add16(packet, 0x0002); pkt_addraw(packet, (unsigned char *) "en", 2); pkt_add16(packet, 0x000E); pkt_add16(packet, 0x0002); pkt_addraw(packet, (unsigned char *) "us", 2); pkt_add16(packet, 0x004A); pkt_add16(packet, 0x0001); pkt_add8(packet, 0x01); snac_sendpkt(handle, 0x17, 0x02, packet, 0); pkt_free(packet); return; } else if (data[3] == 0x03) { TLVLIST *tlv, *trav; uint16_t numtlv, cookie_len = 0; int status = 0; char *bos_server = 0; uint8_t *cookie = 0; numtlv = count_tlv(data + 10, len - 10); tlv = tlv_split(data + 10, len - 10, numtlv); for (trav = tlv; trav != NULL; trav = trav->next) { if (trav->type == 0x0008) { if (((IMCOMM *) handle)->callbacks[IMCOMM_ERROR]) ((IMCOMM *) handle)->callbacks[IMCOMM_ERROR] (handle, IMCOMM_ERROR_INVALID_LOGIN); #ifdef MACINTOSH_CLASSIC #else shutdown(((IMCOMM *) handle)->socket, 0x02); ((IMCOMM *) handle)->socket = -1; #endif } else if (trav->type == 0x0005) { bos_server = malloc(trav->len + 1); memcpy(bos_server, trav->value, trav->len); bos_server[trav->len] = 0; status++; } else if (trav->type == 0x0006) { cookie = malloc(trav->len); cookie_len = trav->len; memcpy(cookie, trav->value, trav->len); status++; } } clear_tlv_list(tlv); if (status == 2) { if (((IMCOMM *) handle)->callbacks[IMCOMM_ERROR]) ((IMCOMM *) handle)->callbacks[IMCOMM_ERROR] (handle, IMCOMM_STATUS_AUTHDONE); bos_signon_phase2(handle, (unsigned char *) bos_server, cookie, cookie_len); free(bos_server); free(cookie); } } } #endif /* MD5_LOGIN */