/** * SIP Parser - Einfacher SIP Message Parser */ #include #include #include #include #include "sip_parser.h" // Hilfsfunktion: Zeile aus Buffer extrahieren static int get_line(const char* buf, size_t buf_len, char* line, size_t line_len) { size_t i = 0; while (i < buf_len && i < line_len - 1) { if (buf[i] == '\r' || buf[i] == '\n') { line[i] = '\0'; // Skip CRLF if (buf[i] == '\r' && i + 1 < buf_len && buf[i + 1] == '\n') { return i + 2; } return i + 1; } line[i] = buf[i]; i++; } line[i] = '\0'; return i; } // Hilfsfunktion: Header-Wert extrahieren static int get_header_value(const char* line, const char* header_name, char* value, size_t value_len) { size_t name_len = strlen(header_name); // Case-insensitive Vergleich if (strncasecmp(line, header_name, name_len) != 0) { return -1; } // Skip Header Name und ':' const char* p = line + name_len; while (*p && (*p == ':' || *p == ' ' || *p == '\t')) { p++; } strncpy(value, p, value_len - 1); value[value_len - 1] = '\0'; return 0; } int sip_parse_message(const char* data, size_t len, sip_message_t* msg) { if (!data || !msg || len == 0) { return -1; } memset(msg, 0, sizeof(sip_message_t)); const char* p = data; size_t remaining = len; char line[512]; // Erste Zeile parsen (Request-Line oder Status-Line) int line_len = get_line(p, remaining, line, sizeof(line)); if (line_len <= 0) return -1; p += line_len; remaining -= line_len; // Status-Line: "SIP/2.0 200 OK" if (strncmp(line, "SIP/2.0 ", 8) == 0) { msg->is_request = false; msg->status_code = atoi(line + 8); // Reason Phrase const char* reason = strchr(line + 8, ' '); if (reason) { strncpy(msg->reason_phrase, reason + 1, sizeof(msg->reason_phrase) - 1); } } // Request-Line: "INVITE sip:user@host SIP/2.0" else { msg->is_request = true; // Method if (strncmp(line, "INVITE ", 7) == 0) { msg->method = SIP_METHOD_INVITE; } else if (strncmp(line, "ACK ", 4) == 0) { msg->method = SIP_METHOD_ACK; } else if (strncmp(line, "BYE ", 4) == 0) { msg->method = SIP_METHOD_BYE; } else if (strncmp(line, "CANCEL ", 7) == 0) { msg->method = SIP_METHOD_CANCEL; } else if (strncmp(line, "REGISTER ", 9) == 0) { msg->method = SIP_METHOD_REGISTER; } else if (strncmp(line, "OPTIONS ", 8) == 0) { msg->method = SIP_METHOD_OPTIONS; } else if (strncmp(line, "INFO ", 5) == 0) { msg->method = SIP_METHOD_INFO; } else { msg->method = SIP_METHOD_UNKNOWN; } // Request URI char* uri_start = strchr(line, ' '); if (uri_start) { uri_start++; char* uri_end = strrchr(uri_start, ' '); if (uri_end) { size_t uri_len = uri_end - uri_start; if (uri_len < sizeof(msg->request_uri)) { strncpy(msg->request_uri, uri_start, uri_len); msg->request_uri[uri_len] = '\0'; } } } } // Headers parsen while (remaining > 0) { line_len = get_line(p, remaining, line, sizeof(line)); if (line_len <= 0) break; p += line_len; remaining -= line_len; // Leere Zeile = Ende der Header, Body beginnt if (line[0] == '\0') { break; } // Header parsen if (get_header_value(line, "Via", msg->via, sizeof(msg->via)) == 0) continue; if (get_header_value(line, "v", msg->via, sizeof(msg->via)) == 0) continue; if (get_header_value(line, "From", msg->from, sizeof(msg->from)) == 0) { sip_extract_tag(msg->from, msg->from_tag, sizeof(msg->from_tag)); continue; } if (get_header_value(line, "f", msg->from, sizeof(msg->from)) == 0) { sip_extract_tag(msg->from, msg->from_tag, sizeof(msg->from_tag)); continue; } if (get_header_value(line, "To", msg->to, sizeof(msg->to)) == 0) { sip_extract_tag(msg->to, msg->to_tag, sizeof(msg->to_tag)); continue; } if (get_header_value(line, "t", msg->to, sizeof(msg->to)) == 0) { sip_extract_tag(msg->to, msg->to_tag, sizeof(msg->to_tag)); continue; } if (get_header_value(line, "Call-ID", msg->call_id, sizeof(msg->call_id)) == 0) continue; if (get_header_value(line, "i", msg->call_id, sizeof(msg->call_id)) == 0) continue; if (strncasecmp(line, "CSeq:", 5) == 0 || strncasecmp(line, "CSeq :", 6) == 0) { const char* cseq_val = strchr(line, ':'); if (cseq_val) { cseq_val++; while (*cseq_val == ' ') cseq_val++; msg->cseq = atoi(cseq_val); // Method aus CSeq const char* method = strchr(cseq_val, ' '); if (method) { method++; if (strncmp(method, "INVITE", 6) == 0) msg->cseq_method = SIP_METHOD_INVITE; else if (strncmp(method, "REGISTER", 8) == 0) msg->cseq_method = SIP_METHOD_REGISTER; else if (strncmp(method, "BYE", 3) == 0) msg->cseq_method = SIP_METHOD_BYE; else if (strncmp(method, "ACK", 3) == 0) msg->cseq_method = SIP_METHOD_ACK; else if (strncmp(method, "CANCEL", 6) == 0) msg->cseq_method = SIP_METHOD_CANCEL; else if (strncmp(method, "OPTIONS", 7) == 0) msg->cseq_method = SIP_METHOD_OPTIONS; } } continue; } if (get_header_value(line, "Contact", msg->contact, sizeof(msg->contact)) == 0) continue; if (get_header_value(line, "m", msg->contact, sizeof(msg->contact)) == 0) continue; if (strncasecmp(line, "Content-Length:", 15) == 0) { const char* cl = strchr(line, ':'); if (cl) msg->content_length = atoi(cl + 1); continue; } if (strncasecmp(line, "l:", 2) == 0) { msg->content_length = atoi(line + 2); continue; } if (get_header_value(line, "Content-Type", msg->content_type, sizeof(msg->content_type)) == 0) continue; if (get_header_value(line, "c", msg->content_type, sizeof(msg->content_type)) == 0) continue; if (get_header_value(line, "WWW-Authenticate", msg->www_authenticate, sizeof(msg->www_authenticate)) == 0) continue; if (get_header_value(line, "Proxy-Authenticate", msg->proxy_authenticate, sizeof(msg->proxy_authenticate)) == 0) continue; } // Body (SDP) if (remaining > 0 && msg->content_length > 0) { size_t body_len = remaining < sizeof(msg->sdp_body) - 1 ? remaining : sizeof(msg->sdp_body) - 1; if (body_len > (size_t)msg->content_length) { body_len = msg->content_length; } strncpy(msg->sdp_body, p, body_len); msg->sdp_body[body_len] = '\0'; if (strstr(msg->content_type, "application/sdp") != NULL) { msg->has_sdp = true; sip_parse_sdp(msg); } } return 0; } int sip_extract_uri(const char* header, char* uri, size_t uri_len) { // Suche oder sip:uri const char* start = strchr(header, '<'); if (start) { start++; const char* end = strchr(start, '>'); if (end) { size_t len = end - start; if (len < uri_len) { strncpy(uri, start, len); uri[len] = '\0'; return 0; } } } // Fallback: sip: suchen start = strstr(header, "sip:"); if (!start) start = strstr(header, "sips:"); if (start) { const char* end = start; while (*end && *end != '>' && *end != ';' && *end != ' ') { end++; } size_t len = end - start; if (len < uri_len) { strncpy(uri, start, len); uri[len] = '\0'; return 0; } } return -1; } int sip_extract_tag(const char* header, char* tag, size_t tag_len) { const char* tag_start = strstr(header, "tag="); if (!tag_start) return -1; tag_start += 4; const char* tag_end = tag_start; while (*tag_end && *tag_end != ';' && *tag_end != '>' && *tag_end != ' ') { tag_end++; } size_t len = tag_end - tag_start; if (len < tag_len) { strncpy(tag, tag_start, len); tag[len] = '\0'; return 0; } return -1; } int sip_extract_display_name(const char* header, char* name, size_t name_len) { // "Display Name" const char* quote_start = strchr(header, '"'); if (quote_start) { quote_start++; const char* quote_end = strchr(quote_start, '"'); if (quote_end) { size_t len = quote_end - quote_start; if (len < name_len) { strncpy(name, quote_start, len); name[len] = '\0'; return 0; } } } // Display Name const char* bracket = strchr(header, '<'); if (bracket && bracket > header) { const char* start = header; while (*start == ' ') start++; size_t len = bracket - start; while (len > 0 && start[len - 1] == ' ') len--; if (len > 0 && len < name_len) { strncpy(name, start, len); name[len] = '\0'; return 0; } } return -1; } int sip_parse_sdp(sip_message_t* msg) { if (!msg->has_sdp || msg->sdp_body[0] == '\0') { return -1; } // RTP Port und IP aus SDP extrahieren // m=audio RTP/AVP // c=IN IP4 const char* m_line = strstr(msg->sdp_body, "m=audio "); if (m_line) { m_line += 8; msg->rtp_port = (uint16_t)atoi(m_line); // Payload Type const char* pt = strstr(m_line, " RTP/AVP "); if (pt) { pt += 9; msg->rtp_payload_type = (uint8_t)atoi(pt); } } const char* c_line = strstr(msg->sdp_body, "c=IN IP4 "); if (c_line) { c_line += 9; const char* end = c_line; while (*end && *end != '\r' && *end != '\n') end++; size_t len = end - c_line; if (len < sizeof(msg->rtp_ip)) { strncpy(msg->rtp_ip, c_line, len); msg->rtp_ip[len] = '\0'; } } return 0; }