void handle_mm(struct session_info *s, struct gsm48_hdr *dtap, unsigned dtap_len, uint32_t fn) { if (dtap_len < sizeof(struct gsm48_hdr)) { SET_MSG_INFO(s, "FAILED SANITY CHECKS (MM_LEN)"); return; } switch (dtap->msg_type & 0x3f) { case 0x01: session_reset(s, 1); s->started = 1; SET_MSG_INFO(s, "IMSI DETACH"); s->detach = 1; s->mo = 1; handle_detach(s, dtap->data); break; case 0x02: SET_MSG_INFO(s, "LOC UPD ACCEPT"); handle_loc_upd_acc(s, dtap->data, dtap_len - 2); break; case 0x04: SET_MSG_INFO(s, "LOC UPD REJECT cause=%d", dtap->data[0]); s->locupd = 1; s->lu_reject = 1; s->lu_rej_cause = dtap->data[0]; s->mo = 1; break; case 0x08: session_reset(s, 1); if (dtap_len < sizeof(struct gsm48_loc_upd_req)) { SET_MSG_INFO(s, "FAILED SANITY CHECKS (LUR_DTAP_SIZE)"); break; } SET_MSG_INFO(s, "LOC UPD REQUEST"); handle_loc_upd_req(s, dtap->data); break; case 0x12: if ((dtap_len > 19) && (dtap->data[17] == 0x20) && (dtap->data[18] == 0x10)) { SET_MSG_INFO(s, "AUTH REQUEST (UMTS)"); s->auth = 2; } else { SET_MSG_INFO(s, "AUTH REQUEST (GSM)"); s->auth = 1; } if (!s->auth_req_fn) { if (fn) { s->auth_req_fn = fn; } else { s->auth_req_fn = GSM_MAX_FN; } } break; case 0x14: if ((dtap_len > 6) && (dtap->data[4] == 0x21) && (dtap->data[5] == 0x04)) { SET_MSG_INFO(s, "AUTH RESPONSE (UMTS)"); if (!s->auth) { s->auth = 2; } } else { SET_MSG_INFO(s, "AUTH RESPONSE (GSM)"); if (!s->auth) { s->auth = 1; } } if (!s->auth_resp_fn) { if (fn) { s->auth_resp_fn = fn; } else { s->auth_resp_fn = GSM_MAX_FN; } } break; case 0x18: switch (dtap->data[0] & GSM_MI_TYPE_MASK) { case GSM_MI_TYPE_IMSI: SET_MSG_INFO(s, "IDENTITY REQUEST, IMSI"); if (s->cipher) { s->iden_imsi_ac = 1; } else { s->iden_imsi_bc = 1; } break; case GSM_MI_TYPE_IMEI: case GSM_MI_TYPE_IMEISV: SET_MSG_INFO(s, "IDENTITY REQUEST, IMEI"); if (s->cipher) { s->iden_imei_ac = 1; } else { s->iden_imei_bc = 1; } break; } break; case 0x19: handle_mi(s, &dtap->data[1], dtap->data[0], 0); switch (dtap->data[1] & GSM_MI_TYPE_MASK) { case GSM_MI_TYPE_IMSI: SET_MSG_INFO(s, "IDENTITY RESPONSE, IMSI %s", s->imsi); if (s->cipher) { s->iden_imsi_ac = 1; } else { s->iden_imsi_bc = 1; } break; case GSM_MI_TYPE_IMEI: case GSM_MI_TYPE_IMEISV: SET_MSG_INFO(s, "IDENTITY RESPONSE, IMEI %s", s->imei); if (s->cipher) { s->iden_imei_ac = 1; } else { s->iden_imei_bc = 1; } break; } break; case 0x1a: SET_MSG_INFO(s, "TMSI REALLOC COMMAND"); s->tmsi_realloc = 1; handle_lai(s, dtap->data, -1); handle_mi(s, &dtap->data[6], dtap->data[5], 1); break; case 0x1b: SET_MSG_INFO(s, "TMSI REALLOC COMPLETE"); s->tmsi_realloc = 1; break; case 0x21: SET_MSG_INFO(s, "CM SERVICE ACCEPT"); s->mo = 1; break; case 0x23: SET_MSG_INFO(s, "CM SERVICE ABORT"); s->mo = 1; break; case 0x24: SET_MSG_INFO(s, "CM SERVICE REQUEST"); session_reset(s, 1); s->started = 1; s->closed = 0; s->serv_req = 1; s->mo = 1; handle_cmreq(s, dtap->data); break; case 0x29: SET_MSG_INFO(s, "ABORT"); s->abort = 1; break; case 0x32: SET_MSG_INFO(s, "MM INFORMATION"); break; default: SET_MSG_INFO(s, "UNKNOWN MM (%02x)", dtap->msg_type & 0x3f); s->unknown = 1; } }
void handle_rr(struct session_info *s, struct gsm48_hdr *dtap, unsigned len, uint32_t fn) { struct gsm48_system_information_type_6 *si6; struct tlv_parsed tp; s->rat = RAT_GSM; assert(s->new_msg); if (!len) { return; } switch (dtap->msg_type) { case GSM48_MT_RR_SYSINFO_1: SET_MSG_INFO(s, "SYSTEM INFO 1"); handle_sysinfo(s, dtap, len); break; case GSM48_MT_RR_SYSINFO_2: SET_MSG_INFO(s, "SYSTEM INFO 2"); handle_sysinfo(s, dtap, len); break; case GSM48_MT_RR_SYSINFO_2bis: SET_MSG_INFO(s, "SYSTEM INFO 2bis"); handle_sysinfo(s, dtap, len); break; case GSM48_MT_RR_SYSINFO_2ter: SET_MSG_INFO(s, "SYSTEM INFO 2ter"); handle_sysinfo(s, dtap, len); break; case GSM48_MT_RR_SYSINFO_2quater: SET_MSG_INFO(s, "SYSTEM INFO 2quater"); handle_sysinfo(s, dtap, len); break; case GSM48_MT_RR_SYSINFO_3: SET_MSG_INFO(s, "SYSTEM INFO 3"); handle_sysinfo(s, dtap, len); break; case GSM48_MT_RR_SYSINFO_4: SET_MSG_INFO(s, "SYSTEM INFO 4"); handle_sysinfo(s, dtap, len); break; case GSM48_MT_RR_SYSINFO_5: SET_MSG_INFO(s, "SYSTEM INFO 5"); rand_check((uint8_t *)dtap, 18, &s->si5, s->cipher); handle_sysinfo(s, dtap, len); break; case GSM48_MT_RR_SYSINFO_5bis: SET_MSG_INFO(s, "SYSTEM INFO 5bis"); rand_check((uint8_t *)dtap, 18, &s->si5bis, s->cipher); handle_sysinfo(s, dtap, len); break; case GSM48_MT_RR_SYSINFO_5ter: SET_MSG_INFO(s, "SYSTEM INFO 5ter"); rand_check((uint8_t *)dtap, 18, &s->si5ter, s->cipher); handle_sysinfo(s, dtap, len); break; case GSM48_MT_RR_SYSINFO_6: SET_MSG_INFO(s, "SYSTEM INFO 6"); rand_check((uint8_t *)dtap, 18, &s->si6, s->cipher); si6 = (struct gsm48_system_information_type_6 *) dtap; handle_lai(s, (uint8_t*)&si6->lai, htons(si6->cell_identity)); handle_sysinfo(s, dtap, len); break; case GSM48_MT_RR_SYSINFO_13: SET_MSG_INFO(s, "SYSTEM INFO 13"); handle_sysinfo(s, dtap, len); break; case GSM48_MT_RR_CHAN_REL: SET_MSG_INFO(s, "CHANNEL RELEASE"); if (s->cipher && !s->fc.enc_rand) s->fc.predict++; s->release = 1; s->rr_cause = dtap->data[0]; if ((len > 3) && ((dtap->data[1] & 0xf0) == 0xc0)) s->have_gprs = 1; session_reset(&s[0], 0); if (auto_reset) { s[1].new_msg = NULL; } break; case GSM48_MT_RR_CLSM_ENQ: SET_MSG_INFO(s, "CLASSMARK ENQUIRY"); break; case GSM48_MT_RR_MEAS_REP: SET_MSG_INFO(s, "MEASUREMENT REPORT"); break; case GSM48_MT_RR_CLSM_CHG: SET_MSG_INFO(s, "CLASSMARK CHANGE"); handle_classmark(s, &dtap->data[1], 2); break; case GSM48_MT_RR_PAG_REQ_1: SET_MSG_INFO(s, "PAGING REQ 1"); handle_paging1(dtap, len); break; case GSM48_MT_RR_PAG_REQ_2: SET_MSG_INFO(s, "PAGING REQ 2"); handle_paging2(dtap, len); break; case GSM48_MT_RR_PAG_REQ_3: SET_MSG_INFO(s, "PAGING REQ 3"); handle_paging3(); break; case GSM48_MT_RR_IMM_ASS: SET_MSG_INFO(s, "IMM ASSIGNMENT"); break; case GSM48_MT_RR_IMM_ASS_EXT: SET_MSG_INFO(s, "IMM ASSIGNMENT EXT"); break; case GSM48_MT_RR_IMM_ASS_REJ: SET_MSG_INFO(s, "IMM ASSIGNMENT REJECT"); break; case GSM48_MT_RR_PAG_RESP: session_reset(s, 1); SET_MSG_INFO(s, "PAGING RESPONSE"); handle_pag_resp(s, dtap->data); break; case GSM48_MT_RR_HANDO_CMD: SET_MSG_INFO(s, "HANDOVER COMMAND"); parse_assignment(dtap, len, s->cell_arfcns, &s->ga); s->handover = 1; s->use_jump = 2; break; case GSM48_MT_RR_HANDO_COMPL: SET_MSG_INFO(s, "HANDOVER COMPLETE"); break; case GSM48_MT_RR_ASS_CMD: SET_MSG_INFO(s, "ASSIGNMENT COMMAND"); if ((s->fc.enc-s->fc.enc_null-s->fc.enc_si) == 1) s->forced_ho = 1; parse_assignment(dtap, len, s->cell_arfcns, &s->ga); s->assignment = 1; s->use_jump = 1; break; case GSM48_MT_RR_ASS_COMPL: SET_MSG_INFO(s, "ASSIGNMENT COMPLETE"); s->assign_complete = 1; break; case GSM48_MT_RR_CIPH_M_COMPL: SET_MSG_INFO(s, "CIPHER MODE COMPLETE"); if (s->cipher_missing < 0) { s->cipher_missing = 0; } else { s->cipher_missing = 1; } if (!s->cm_comp_first_fn) { if (fn) { s->cm_comp_first_fn = fn; } else { s->cm_comp_first_fn = GSM_MAX_FN; } } if (fn) { s->cm_comp_last_fn = fn; } else { s->cm_comp_last_fn = GSM_MAX_FN; } s->cm_comp_count++; if (dtap->data[0] == 0x2b) return; /* get IMEISV */ tlv_parse(&tp, &gsm48_rr_att_tlvdef, dtap->data, len-2, 0, 0); if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) { uint8_t *v = (uint8_t *) TLVP_VAL(&tp, GSM48_IE_MOBILE_ID); handle_mi(s, &v[1], v[0], 0); s->cmc_imeisv = 1; } break; case GSM48_MT_RR_GPRS_SUSP_REQ: SET_MSG_INFO(s, "GPRS SUSPEND"); s->have_gprs = 1; //tlli //rai (lai+rac) break; case GSM48_MT_RR_CIPH_M_CMD: if (!s->cm_cmd_fn) { if (fn) { s->cm_cmd_fn = fn; } else { s->cm_cmd_fn = GSM_MAX_FN; } } if (dtap->data[0] & 1) { s->cipher = 1 + ((dtap->data[0]>>1) & 7); if (!not_zero(s->key, 8)) s->decoded = 0; } SET_MSG_INFO(s, "CIPHER MODE COMMAND, A5/%u", s->cipher); if (dtap->data[0] & 0x10) { s->cmc_imeisv = 1; if (s->cipher && !s->fc.enc_rand) s->fc.predict++; } s->cipher_missing = -1; break; case 0x60: SET_MSG_INFO(s, "UTRAN CLASSMARK"); break; default: SET_MSG_INFO(s, "UNKNOWN RR (%02x)", dtap->msg_type); s->unknown = 1; }
void* session_worker(void* data) { int capacity = 1; int timeout = 30000; // 30 seconds // setup session data container struct session_data * session = (struct session_data*) data; int sockfd = session->sockfd; session_reset(session); printf("session %d: starting work, state %d\n", sockfd, session->state); REPLY_TO_CLIENT(sockfd, REPLY_GREET); // buffer for receiving messages int buffer_size = 1024; char* buffer = calloc(buffer_size, sizeof(char)); // epoll setup int epoll_set = epoll_create(capacity); struct epoll_event listen_event; listen_event.data.fd = sockfd; listen_event.events = EPOLLIN; epoll_ctl(epoll_set, EPOLL_CTL_ADD, sockfd, &listen_event); struct epoll_event* caught_events = calloc(capacity, sizeof(struct epoll_event)); while (session->state != SESSION_QUIT) { // we wait for some messages int nfds = epoll_wait(epoll_set, caught_events, 1, timeout); if (nfds == 0) { // timeout close(sockfd); printf("session %d: timeout, work is over\n", sockfd); break; } // we parse our messages, we assume only one event happens if (nfds == 1) { memset(buffer, 0, BUFFER_SIZE); int received_bytes = recv(sockfd, buffer, buffer_size - 1, 0); // connection closed if (received_bytes == 0) { printf("session %d: client closed connection\n", sockfd); break; } // errors if (received_bytes < 0) { printf("session %d: recv error\n", sockfd); break; } // form a request structure using the received message struct request* req = parse_request(buffer); // dont interpret text if we're in DATA state if (session->state == SESSION_DATA && req->command != CMD_CRLF) { // add line to body if (!session->message->body) { session->message->body = strdup(buffer); } else { // gotta love C int size = strlen(buffer) + strlen(session->message->body) + 1; char * extended_message = (char*) malloc(sizeof(char) * size); strncpy(extended_message, session->message->body, strlen(session->message->body)+1); strncat(extended_message, buffer, strlen(buffer)); extended_message[size] = '\0'; free(session->message->body); session->message->body = extended_message; } if (req->arguments) { free(req->arguments); } free(req); continue; } if (session->state == SESSION_DATA && req->command == CMD_CRLF && (strlen(buffer) > 3)) { // add line to body if (!session->message->body) { session->message->body = strdup(buffer); } else { // gotta love C int size = strlen(buffer) + strlen(session->message->body) + 1; char * extended_message = (char*) malloc(sizeof(char) * size); strncpy(extended_message, session->message->body, strlen(session->message->body)+1); strncat(extended_message, buffer, strlen(buffer)-5); extended_message[size] = '\0'; free(session->message->body); session->message->body = extended_message; } if (req->arguments) { free(req->arguments); } } // fill session data according to request switch(req->command) { case CMD_HELO: // expect a helo at start; a helo resets session; helo expects 1 argument: domain session_reset(session); if (session->state == SESSION_NEW) { REPLY_TO_CLIENT(sockfd, REPLY_OK); } session->state = SESSION_GREET; session->domain = strdup((char*) (req->arguments)); printf("session %d: state changed to greeted\n", sockfd); break; case CMD_MAIL: // mail from expects an argument: sender address; // can be after a helo or transaction completion if (session->state == SESSION_GREET) { session->state = SESSION_MAIL; session->message->mail_from = strdup((char*) (req->arguments)); REPLY_TO_CLIENT(sockfd, REPLY_OK); printf("session %d: got mail from\n", sockfd); } else { REPLY_TO_CLIENT(sockfd, REPLY_OOO); } break; case CMD_RCPT: // rcpt to expects an argument: receipient address; // can be after a mail from or another rcpt to command if (session->state == SESSION_MAIL || session->state == SESSION_RCPT) { session->state = SESSION_RCPT; session->message->rcpt_to = strdup((char*) (req->arguments)); REPLY_TO_CLIENT(sockfd, REPLY_OK); printf("session %d: state changed to got receipients\n", sockfd); } else { REPLY_TO_CLIENT(sockfd, REPLY_OOO); } break; case CMD_DATA: // no arguments; must be after a rcpt to command if (session->state == SESSION_RCPT) { session->state = SESSION_DATA; REPLY_TO_CLIENT(sockfd, REPLY_DATA_INFO); printf("session %d: state changed to data receival\n", sockfd); } else { REPLY_TO_CLIENT(sockfd, REPLY_OOO); } break; case CMD_CRLF: // signifies an end of a message's body; // only after data command was invoked if (session->state == SESSION_DATA) { session_submit(session); session_reset(session); session->state = SESSION_GREET; REPLY_TO_CLIENT(sockfd, REPLY_OK); printf("session %d: data transaction over, state changed to greeted\n", sockfd); } else { REPLY_TO_CLIENT(sockfd, REPLY_OOO); } break; case CMD_QUIT: // closes session session->state = SESSION_QUIT; REPLY_TO_CLIENT(sockfd, REPLY_BYE); printf("session %d: state changed to quit\n", sockfd); break; default: // send syntax error REPLY_TO_CLIENT(sockfd, REPLY_SYNTAX_ERROR); printf("session %d: syntax error\n", sockfd); break; } free(req); } } free(buffer); free(data); printf("session %d: finished\n", sockfd); close(sockfd); return NULL; }