static int cf_call_find_matchtags = TRUE; /* configuration of tag matching */ struct sip_request { int type; struct sockaddr_in addr; /* address received from */ char* cmd; /* first line command */ char* from; char* to; char* callid; char* cseq; char* date; char* contact; int seqno; int respcode; char tagt[SIP_LEN_BUF_S]; char tagf[SIP_LEN_BUF_S]; char firstline[SIP_LEN_BUF_S]; /* copy of first line header */ char* rlPart1; /* SIP Method Name or "SIP/2.0" protocol version */ char* rlPart2; /* The Request URI or Response Status */ int len; int headers; /* number of SIP headers */ char* header[SIP_MAX_HEADERS]; /* headers */ int lines; /* number of lines of content */ char* line[SIP_MAX_LINES]; /* lines */ char data[SIP_LEN_PACKET_MAX]; int parsed; struct sip_pvt* call; /* the call that we matched this message to */ int matchtags; /* save the results from the tag matching */ }; /* * Manage SIP Interfaces (Calls) ******************************************************** * * call_create: Create the structures for a SIP end point * i.e. allocate the PVT and initialize it * * call_destroy: destroy an endpoint, called by user with locks held on the list * call_destroy__: destroy an endpoint, do the actual work * call_destroy_sched: schedule the destruction of an endpoint * __call_destroy_auto: service routine for destroying a scheduled destruction * call_destroy_cancel: cancel a scheduled destruction * * call_find: match a call with one in our list given the callid and tag */ /* * call_find: match an incoming message to an existing call * * return: bitmask of test results * 0 if call ID was not found * >0 if call ID was found * x|x|x|1 (1) we didn't test tags * x|x|1|x (2) we tested tags * x|1|1|x (4+2) local tag matched * 1|x|1|x (8+2) remote tag matched * if a tag to be matched is missing, then this is * assumed to be a match */ enum { TAGS_ID = 1<<0, TAGS_ID_ONLY = 1<<1, TAGS_LOCAL = 1<<2, TAGS_REMOTE = 1<<3, }; static int call_find(struct sip_request* req) { struct sip_pvt* p; char* tagr; /* remote tag */ char* tagl; /* local tag */ req->call = NULL; req->matchtags = 0; if (!req->callid || ast_strlen_zero(req->callid)) { ast_log(LOG_WARNING, "%s: Message from %s has no Call-ID\n", mod_name, inet_ntoa(req->addr.sin_addr)); return req->matchtags; } tagr = (req->type==SIP_RESPONSE) ? req->tagt : req->tagf; tagl = (req->type==SIP_RESPONSE) ? req->tagf : req->tagt; ast_mutex_lock(&iflock); p = iflist; while (p) { if (!strcmp(p->callid, req->callid)) { BOOL matchtags = p->peer ? p->peer->matchtags : cf_call_find_matchtags; req->matchtags = TAGS_ID; if (!matchtags) goto found; /* if we shouldn't match tags, simply return */ /* All this needs to be re-examined, because it's not right, nore do others do it always right */ #if 1 /* for debugging */ if (matchtags > 1) ast_log(LOG_DEBUG, "%s: Found existing call for %s\n" "\t\tCall-ID = %s\n" "\t\tttag = %s\n" "\t\tftag = %s\n" "\t\tourtag = %s\n" "\t\ttheirtag = %s\n", mod_name, req->type==SIP_RESPONSE ? "RESPONSE" : "REQUEST", req->callid, req->tagt, req->tagf, p->tag, p->theirtag ); #endif req->matchtags = TAGS_ID_ONLY; if (ast_strlen_zero(p->tag) || ast_strlen_zero(tagl) || !strcmp(p->tag, tagl)) req->matchtags |= TAGS_LOCAL; if (ast_strlen_zero(p->theirtag) || ast_strlen_zero(tagr) || !strcmp(p->theirtag, tagr)) req->matchtags |= TAGS_REMOTE; goto found; /* Found the call */ } p = p->next; } #if 1 /* for debugging */ ast_log(LOG_DEBUG, "%s: Couldn't match incoming %s message to any existing call:\n" "\t\tCall-ID = %s\n" "\t\tttag = %s\n" "\t\tftag = %s\n", mod_name, req->type==SIP_RESPONSE ? "RESPONSE" : "REQUEST", req->callid, req->tagt, req->tagf ); #endif found: req->call = p; ast_mutex_unlock(&iflock); return req->matchtags; } /* * sip_sock_read: Read packets from our listening SIP socket * All external incoming requests start here */ static int sip_sock_read(int *id, int fd, short events, void *ignore) { enum { RUN_NOT_AGAIN=0, RUN_AGAIN=1 }; struct sip_request req; struct sip_pvt *p; int res, len; int nounlock; int recount = 0; int debug; char debug_buf[SIP_LEN_PACKET_MAX]; BOOL ok; memset(&req, 0, sizeof(req)); len = sizeof(req.addr); STATISTICS(recv); res = recvfrom(sip_sock, req.data, sizeof(req.data), 0, (struct sockaddr *)&req.addr, &len); if (res < 0) { STATISTICS(recv_err); ast_log(LOG_ERROR, "%s: recvfrom error: %s %s\n", mod_name, sprint_error(res), strerror(errno)); return RUN_AGAIN; } else if (res == sizeof(req.data)){ ast_log(LOG_WARNING, "%s: recfrom may have received more data than able to process, size=%d\n", mod_name, res ); } if (len != sizeof(req.addr)){ ast_log(LOG_WARNING, "%s: recfrom returned a different address size (%d)\n", mod_name, len); } debug=sip_debug_test_addr(&req.addr); req.data[res] = '\0'; req.len = res; if (debug && (sip_verbose > vbl3)){ memcpy(debug_buf, req.data, req.len+1); } if (cf_pedanticsipchecking){ req.len = lws2sws(req.data, req.len); } ok = sip_parse(&req); if (debug) { char tmp[SIP_LEN_SOCKPRINT]; struct in_addr dummy; BOOL nat_l; nat_l = sip_use_externip(&req.addr.sin_addr, &dummy); if (sip_verbose > vbl3){ /* full trace */ ast_verbose(FORMAT_rx, mod_name, nat_l?"N":" ", /* local nat */ " ", /* remote nat */ " ", //p->rtpsym?"S":" ", sprint_sockaddr(tmp, req.addr), dbg_pkt_stamp(), res, req.headers, req.lines, debug_buf, FORMAT_rx_tail); } else { /* header trace only */ ast_verbose(FORMAT_rx3, mod_name, nat_l?"N":" ", /* local nat */ " ", /* remote nat */ " ", //p->rtpsym?"S":" ", sprint_sockaddr(tmp, req.addr), res, req.headers, req.lines, req.data); } } if (req.headers < 2) { STATISTICS(recv_err); if (sip_verbose >= vblx) ast_log(LOG_WARNING, "%s: Received invalid request. We must have at least two headers\n", mod_name); return RUN_AGAIN; } if (!ok) return RUN_AGAIN; retrylock: /* Process message, with netlock held */ ast_mutex_lock(&netlock); /* See if we have a call that this message belongs to */ res = call_find(&req); if (res) { /* we matched the Call-Id */ p = req.call; if (debug && sip_verbose > 3) { if (req.matchtags & TAGS_LOCAL) ast_log(LOG_DEBUG, "%s: matched local tags for message\n", mod_name); if (req.matchtags & TAGS_REMOTE) ast_log(LOG_DEBUG, "%s: matched remote tags for message\n", mod_name); } if (req.matchtags == TAGS_ID_ONLY) { /* send error message and abort when we are checking the tags strictly */ /* and couldn't match the tags */ ast_log(LOG_DEBUG, "%s: matched no tags for message\n", mod_name); #if 1 /* lets try this for a while */ transmit_response(p, "481 Call leg/transaction does not exist", req); #endif } /* there are other matching scenarios upon which we can take action */ /* but let's observe traffic until the day comes */ } else { /* no, create new call */ /* we are deferring RTP allocation until we know that we will need it */ p = call_create(req.callid, &req.addr, ALLOC_WITH_GLOBAL_NAT, ALLOC_DEFER_RTP); } if (!p) { ast_log(LOG_ERROR, "%s: Cannot find or create a call for incoming message\n", mod_name); return RUN_AGAIN; } ast_mutex_lock(&p->lock); /* lock it */ /* We now have a call that this packet belongs to */ /* lock the owner if it has one -- we may need it */ if (p->owner && ast_mutex_trylock(&p->owner->lock)) { ast_log(LOG_DEBUG, "%s: Failed to grab owner lock for incoming message, trying again...\n", mod_name); ast_mutex_unlock(&p->lock); ast_mutex_unlock(&netlock); usleep(1); /* Sleep short amount of time */ goto retrylock; } memcpy(&p->recv, &req.addr, sizeof(struct sockaddr_in)); // append_history(p, "Rx", req.data); if (cf_recordhistory) { char tmp[SIP_LEN_BUF_S] = ""; /* This is a response, note what it was for */ snprintf(tmp, sizeof(tmp), "%s / %s", req.data, req.cseq); append_history(p, "Rx", tmp); } nounlock = 0; strncpy(p->peeruseragent, get_header(&req, "User-Agent"), sizeof(p->peeruseragent)-1); if (req.type == SIP_RESPONSE) { /* RESPONSE */ STATISTICS(recv_response); res = handle_response(p, &req); if (res != OK) { STATISTICS(recv_response_err); } } else { /* REQUEST */ STATISTICS(recv_request); res = handle_request(p, &req, &recount, &nounlock); if (res != OK) { STATISTICS(recv_request_err); } } if (p->owner && !nounlock) ast_mutex_unlock(&p->owner->lock); ast_mutex_unlock(&p->lock); ast_mutex_unlock(&netlock); if (recount) ast_update_use_count(); return RUN_AGAIN; }