From patchwork Mon Feb 12 23:08:24 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tiago Lam X-Patchwork-Id: 872510 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="d342YrIQ"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zgLwz5BWDz9sRm for ; Tue, 13 Feb 2018 10:10:11 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id DDCB41048; Mon, 12 Feb 2018 23:09:09 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id F0A52FA3 for ; Mon, 12 Feb 2018 23:09:07 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-wm0-f67.google.com (mail-wm0-f67.google.com [74.125.82.67]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 69350D0 for ; Mon, 12 Feb 2018 23:09:06 +0000 (UTC) Received: by mail-wm0-f67.google.com with SMTP id r71so12580051wmd.1 for ; Mon, 12 Feb 2018 15:09:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=nY+AQmBS1OcFYJb9359gTHdlMtYcfwKZt8qLOcJvnkA=; b=d342YrIQodMLzoriY5Q6fbpNliNSnwDvpuXSmZ+Ubk61N7qbF+8rhJR6yLlNWUUgFv eBFVAL31/ZJrvuoWglEN82Rp1TDtd/6Y+uqu+GnowMqtFSjYX0cMrD+BdzFjBKhE4tt0 7EipG0x2A+juuYnvzd0EmMTUQUrnOgJ69PdkeD6QqsvVOHE2Iwmk2Tn7ePNqGjV1eYUP +AKaJ2ZZmPXIyQobQiADGfXZpyW00Nofy69B+payNxoyUCvU1HjCo/cuUi5QyvreAaoC IQ5/PqVRJbwLF73n736H0n92op+i9A0jI1LTQ3SWwYFQ0t32yZ801/mcZWDpu3zJyrTo AUrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=nY+AQmBS1OcFYJb9359gTHdlMtYcfwKZt8qLOcJvnkA=; b=f6kINyrA8cSxi1A3Pcdbid8qCWOn8t0M06nyVsKVAdHQAOFJgLz4GKZ7R4ZHmj1K/P w3/GICMRf3Yq5kKB7rptg43uwTQnfF0wzRQghC0jEzOzuWQrAZ9xBvELT8MXXFIDctd5 +wbLq+yDRmqVZNlMOFkxz1rYSEPghcWFSSlL1wQzKdqoemuXj5uKeuvcS5uGm9+7eIqW P6pAoRBgXOuJxUBE/mLlLE697HbvOi5fTNx3x/3C6g3WAlpwPNsvLtgfSEcAIrJ4FMRO gNNov6mv34mZwbwnp8KytDq6wBc+yD/rskMT9dtcbCYSUBsU5UigeKjuh8rTh/A/GNWQ zufg== X-Gm-Message-State: APf1xPCZ8l6d+1dy4gj14pVWjfMP2iXGRTksmQgXWZTudHeI+MP13WP8 sZHTrIF9miAXZKhamf2B9xK3wvE= X-Google-Smtp-Source: AH8x224BKyOv07RXZJxsEgegqJLO9UJONLbjAHzSlL3hAN3wBSeFuhMTole6hWIkcXOIa1+/nYsKoA== X-Received: by 10.28.129.70 with SMTP id c67mr123294wmd.160.1518476944061; Mon, 12 Feb 2018 15:09:04 -0800 (PST) Received: from elche.localdomain (79-66-214-20.dynamic.dsl.as9105.com. [79.66.214.20]) by smtp.googlemail.com with ESMTPSA id z73sm16285673wrb.50.2018.02.12.15.09.02 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Mon, 12 Feb 2018 15:09:02 -0800 (PST) From: Tiago Lam To: dev@openvswitch.org Date: Mon, 12 Feb 2018 23:08:24 +0000 Message-Id: <20180212230829.31624-2-tiagolam@gmail.com> X-Mailer: git-send-email 2.14.3 In-Reply-To: <20180212230829.31624-1-tiagolam@gmail.com> References: <20180212230829.31624-1-tiagolam@gmail.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [RFC PATCH v2 1/6] Conntrack: Add new API for future SIP Alg. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org The new API (in conntrack-sip.h and conntrack-sip.c) defines preliminary functions, helpers and strucures to move through and parse SIP messages, including SDP in the message bodies, if present. Note that this is still a preliminary version of the API, and future work is still needed to improve the SIP and SDP handling. When parsing an SDP, for example, the order of the 'm', 'c' and 'o' sections is not enforced, and only the ports and IP addresses are stored at the moment. This API will be used by a future commit in order to implement the new SIP Alg in userspace conntrack. Signed-off-by: Tiago Lam --- lib/automake.mk | 2 + lib/conntrack-sip.c | 477 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/conntrack-sip.h | 112 ++++++++++++ 3 files changed, 591 insertions(+) create mode 100644 lib/conntrack-sip.c create mode 100644 lib/conntrack-sip.h diff --git a/lib/automake.mk b/lib/automake.mk index 38d2a999d..1afdb5756 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -51,6 +51,8 @@ lib_libopenvswitch_la_SOURCES = \ lib/connectivity.h \ lib/conntrack-icmp.c \ lib/conntrack-private.h \ + lib/conntrack-sip.c \ + lib/conntrack-sip.h \ lib/conntrack-tcp.c \ lib/conntrack-other.c \ lib/conntrack.c \ diff --git a/lib/conntrack-sip.c b/lib/conntrack-sip.c new file mode 100644 index 000000000..7239bdad6 --- /dev/null +++ b/lib/conntrack-sip.c @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2017 Nicira, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "conntrack-sip.h" +#include "dp-packet.h" +#include "util.h" + +enum sip_mthd sip_mthd_to_enum(char *mthd) { + if (mthd == NULL) { + return INVALID; + } + + if (!strcmp(mthd, "INVITE")) { + return INVITE; + } else if (!strcmp(mthd, "REGISTER")) { + return REGISTER; + } else if (!strcmp(mthd, "ACK")) { + return ACK; + } else if (!strcmp(mthd, "CANCEL")) { + return CANCEL; + } else if (!strcmp(mthd, "BYE")) { + return BYE; + } else if (!strcmp(mthd, "OPTIONS")) { + return OPTIONS; + } + + return INVALID; +} +/* A valid SIP status falls in between the following ranges (rfc3261): + * - 1xx: Provisional; + * - 2xx: Success; + * - 3xx: Redirection; + * - 4xx: Client Error; + * - 5xx: Server Error; + * - 6xx: Global Failure. + * + * Returns false to anything that falls outside of the ranges above, and true + * otherwise. */ +bool is_valid_status(uint32_t status) { + if ((status >= 100 && status < 200) || + (status >= 200 && status < 300) || + (status >= 300 && status < 400) || + (status >= 400 && status < 500) || + (status >= 500 && status < 600) || + (status >= 600 && status < 700)) { + return true; + } + + return false; +} + +/* Find and validate the beginning of the CRLF ending, returning a pointer to + * the found CR, or NULL otherwise. */ +char * sip_ln_find_crlf(char *sip, size_t len) { + if (sip == NULL) { + return NULL; + } + + char *cr = memchr(sip, '\r', len); + if (cr == NULL) { + return NULL; + } + char *lf = memchr(sip, '\n', len); + if (lf == NULL) { + return NULL; + } + + if (lf - cr != 1) { + return NULL; + } + + return cr; +} + +/* Returns the size of the line until the next CRLF ending (not inclusive). If + * CRLF is not found it returns -1. */ +size_t sip_ln_len(char *sip, size_t len) { + char *cr = sip_ln_find_crlf(sip, len); + if (cr == NULL) { + return -1; + } + + return cr - sip; +} + +/* Ignores the next CRLF ending and moves to CRLF + 1. After returning, 'sip' + * is modified to point to the next line after CRLF (CRLF + 1), and 'len' has + * been updated accordingly. If CRLF is not found, 'sip' and 'len' are + * untouched */ +void sip_ln_skip(char **sip, size_t *len) { + char *cr = sip_ln_find_crlf(*sip, *len); + if (cr == NULL) { + return; + } + size_t sz = cr - *sip; + *sip += sz + 2; + *len -= sz + 2; +} + +/* Iterate through each word in the line at 'sip', where each word is divided + * by the space char ' ', and store the resulting word in 'dst', if not NULL. + * + * After returning 'sip' is pointing to the next word in the line, and 'len' + * has been updated accordingly. */ +bool sip_ln_cpy_nxt_word(char **sip, size_t *len, char **dst) { + char *s; + + s = memchr(*sip, ' ', *len); + if (s == NULL) { + return false; + } + + size_t wlen = s - *sip; + if (dst != NULL) { + /* Store found word int the pointed to 'dst' */ + *dst = xmalloc(sizeof(char) * (wlen + 1)); + ovs_strzcpy(*dst, *sip, wlen + 1); + } + + /* Move 'sip' to the beginning of next word */ + *sip += wlen + 1; + *len -= wlen + 1; + + return true; +} + +/* Parse an 'o' line from the SDP. + * + * 'conn' should point to the beginning of the line, after 'o='. + * 'len' is the size the 'o' line, excluding 'o=' and the CRLF ending. + * + * XXX Returns only the origin's IP address for now. + * + * Example: o=user1 53655765 2353687637 IN IP4 127.0.1.1\r\n */ +char * sdp_get_origin(char *orig, size_t len) { + char *o_ln = orig; + char *ip_addr; + + for (int i = 0; i < 5; i ++) { + if (!sip_ln_cpy_nxt_word(&o_ln, &len, NULL)) { + return NULL; + } + } + + ip_addr = xmalloc(sizeof(char) * (len + 1)); + ovs_strzcpy(ip_addr, o_ln, len + 1); + + return ip_addr; +} + +/* Parse an 'c' line from the SDP. + * + * 'conn' should point to the beginning of the line, after 'c='. + * 'len' is the size the 'c' line, excluding 'c=' and the CRLF ending. + * + * XXX Returns only the connection's IP address for now. + * + * Example: c=IN IP4 10.0.2.128\r\n */ +char * sdp_get_conn(char *conn, size_t len) { + char *c_ln = conn; + char *ip_addr; + + for (int i = 0; i < 2; i ++) { + if (!sip_ln_cpy_nxt_word(&c_ln, &len, NULL)) { + return NULL; + } + } + + ip_addr = xmalloc(sizeof(char) * (len + 1)); + ovs_strzcpy(ip_addr, c_ln, len + 1); + + return ip_addr; +} + +/* Parse an 'm' line from the SDP. + * + * 'media' should point to the beginning of the line, after 'm='. + * 'len' is the size the 'm' line, excluding 'm=' and the CRLF ending. + * + * XXX Returns only the media's port. + * + * Example: m=audio 8192 RTP/AVP 0 */ +char * sdp_get_media(char *media, size_t len) { + char *m_ln = media; + char *port; + + if (!sip_ln_cpy_nxt_word(&m_ln, &len, NULL)) { + return NULL; + } + + if (!sip_ln_cpy_nxt_word(&m_ln, &len, &port)) { + return NULL; + } + + return port; +} + +/* Given a pointer to the beginning of an SDP, parses it accordingly. + * + * 'sdp' should point to the beginning of the SDP. + * 'len' should be the size of the whole SIP message after start-line and + * headers have been parsed. + * + * Returns a 'sip_sdp' struct, which the caller is responsible for freeing. + * XXX No order is enforced and only supports one 'm' line for now */ +struct sip_sdp * sip_parse_sdp(char *sdp, size_t len) { + char *sip = sdp; + char *addr_s = NULL; + char *port_s = NULL; + uint32_t hport; + uint32_t haddr; + struct sip_sdp *sip_sdp; + size_t ln_len; + + if (sip == NULL) { + return NULL; + } + + sip_sdp = xmalloc(sizeof(struct sip_sdp)); + + for (int i = 0; i < len; i++) { + ln_len = sip_ln_len(sip, len); + if (ln_len == -1) { + /* Message is invalid, most likely no CRLF found */ + goto error; + } + + switch (sip[0]) { + case 'o': + addr_s = sdp_get_origin(sip + 2, ln_len - 2); + if (!ip_parse(addr_s, &haddr)) { + free(addr_s); + goto error; + } + sip_sdp->orig = ntohl(haddr); + free(addr_s); + break; + case 'c': + addr_s = sdp_get_conn(sip + 2, ln_len - 2); + if (!ip_parse(addr_s, &haddr)) { + free(addr_s); + goto error; + } + sip_sdp->conn = ntohl(haddr); + free(addr_s); + break; + case 'm': + port_s = sdp_get_media(sip + 2, ln_len - 2); + if (!str_to_uint(port_s, 10, &hport)) { + free(port_s); + goto error; + } + sip_sdp->port = hport; + free(port_s); + break; + } + + sip_ln_skip(&sip, &len); + } + + return sip_sdp; + +error: + free(sip_sdp); + + return NULL; +} + +/* generic-message = start-line + * *message-header + * CRLF + * [ message-body ] + * start-line = Request-Line / Status-Line */ +int sip_skip_to_body(char **sip_hdr, size_t *len) { + if (*sip_hdr == NULL) { + return -EINVAL; + } + + char *cr; + int sz; + while (*len >= 2) { + /* Detect the lone CRLF which divides the SIP message header from the + * the SIP message body */ + if ((*sip_hdr)[0] == '\r' && (*sip_hdr)[1] == '\n') { + /* Skip 2 bytes for CRLF */ + *sip_hdr += 2; + *len -= 2; + return 0; + } + + cr = sip_ln_find_crlf(*sip_hdr, *len); + if (cr == NULL) { + return -EINVAL; + } + sz = cr - *sip_hdr; + *sip_hdr += sz + 2; + *len -= sz + 2; + } + + return -EINVAL; +} + +void free_strt_ln(struct sip_strt_ln *strt_ln) { + if (strt_ln->type == STATUS_LINE) { + free_stat_ln(strt_ln->stat_ln); + } else { + free_reqs_ln(strt_ln->reqs_ln); + } + + free(strt_ln); +} + +/* Given a pointer to the beginning of the SIP message (start-line), detects + * if it is a request-line or a status-line and parses accordingly. + * + * 'start_ln' should point to the beginning of the SIP message. + * 'len' may be the size of the whole SIP message, but must be at least the + * size of the start-line, including with the CRLF ending. + * + * Returns a 'sip_strt_ln' struct, which the caller is responsible for + * freeing. */ +struct sip_strt_ln * +sip_parse_strt_ln(char *strt_ln_str, size_t len) { + struct sip_strt_ln *strt_ln; + size_t ln_len; + + /* Calculate the length of the SIP star-line */ + ln_len = sip_ln_len(strt_ln_str, len); + if (ln_len == -1) { + /* Message is invalid, most likely no CRLF found */ + return NULL; + } + + strt_ln = xzalloc(sizeof(struct sip_strt_ln)); + + if (!strncmp(strt_ln_str, "SIP", 3)) { + struct sip_stat_ln *stat_ln = sip_parse_stat_ln(strt_ln_str, ln_len); + if (stat_ln == NULL) { + goto error; + } + + strt_ln->stat_ln = stat_ln; + strt_ln->type = STATUS_LINE; + } else if (!strncmp(strt_ln_str, "INVITE", 6) || + !strncmp(strt_ln_str, "ACK", 3) || + !strncmp(strt_ln_str, "BYE", 3)) { + struct sip_reqs_ln *reqs_ln = sip_parse_reqs_ln(strt_ln_str, ln_len); + if (reqs_ln == NULL) { + goto error; + } + + strt_ln->reqs_ln = reqs_ln; + strt_ln->type = REQUEST_LINE; + } else { + /* Message not yet supported */ + goto error; + } + + return strt_ln; + +error: + free(strt_ln); + + return NULL; +} + +void free_stat_ln(struct sip_stat_ln *stat_ln) { + free(stat_ln->ver); + free(stat_ln->reason); + free(stat_ln); +} + +/* Parses the SIP status-line. + * + * 'start_ln' should point to the beginning of the SIP message. + * 'len' is the size of the status-line, not including the CRLF ending. + * + * Returns a 'sip_stat_ln' struct, which the caller is responsible for freeing. + * + * Example SIP status-line: SIP/2.0 200 OK\r\n */ +struct sip_stat_ln * +sip_parse_stat_ln(char *start_ln, size_t len) { + struct sip_stat_ln *stat_ln; + + stat_ln = xzalloc(sizeof(struct sip_stat_ln)); + + if (!sip_ln_cpy_nxt_word(&start_ln, &len, &stat_ln->ver)) { + goto error; + } + + char *status = NULL; + + if (!sip_ln_cpy_nxt_word(&start_ln, &len, &status)) { + free(status); + goto error; + } + + if (!str_to_uint(status, 10, &stat_ln->status) || + !is_valid_status(stat_ln->status)) { + free(status); + goto error; + } + + stat_ln->reason = xmalloc(sizeof(char) * (len + 1)); + ovs_strzcpy(stat_ln->reason, start_ln, len + 1); + + return stat_ln; + +error: + free_stat_ln(stat_ln); + + return NULL; +} + +void free_reqs_ln(struct sip_reqs_ln *reqs_ln) { + free(reqs_ln->req_uri); + free(reqs_ln->ver); + free(reqs_ln); +} + +/* Parses the SIP request-line. + * + * 'start_ln' should point to the beginning of the SIP message. + * 'len' is the size of the request-line, not including the CRLF ending. + * + * Returns a 'sip_reqs_ln' struct, which the caller is responsible for freeing. + * + * Example SIP request-line: INVITE sip:service@10.0.1.128:5060 SIP/2.0\r\n */ +struct sip_reqs_ln * +sip_parse_reqs_ln(char *start_ln, size_t len) { + struct sip_reqs_ln *reqs_ln; + char *sip_mthd = NULL; + + reqs_ln = xzalloc(sizeof(struct sip_reqs_ln)); + + if (!sip_ln_cpy_nxt_word(&start_ln, &len, &sip_mthd)) { + goto error; + } + + reqs_ln->mthd = sip_mthd_to_enum(sip_mthd); + + if (reqs_ln->mthd == INVALID) { + goto error; + } + + if (!sip_ln_cpy_nxt_word(&start_ln, &len, &reqs_ln->req_uri)) { + goto error; + } + + reqs_ln->ver = xmalloc(sizeof(char) * (len + 1)); + ovs_strzcpy(reqs_ln->ver, start_ln, len + 1); + + return reqs_ln; + +error: + free(sip_mthd); + free_reqs_ln(reqs_ln); + + return NULL; +} diff --git a/lib/conntrack-sip.h b/lib/conntrack-sip.h new file mode 100644 index 000000000..134f35f3a --- /dev/null +++ b/lib/conntrack-sip.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017 Nicira, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "dp-packet.h" + +struct sip_sdp { + uint32_t orig; + uint32_t conn; + uint16_t port; +}; + +enum sip_mthd { + INVITE, + REGISTER, + ACK, + CANCEL, + BYE, + OPTIONS, + INVALID, +}; + +struct sip_reqs_ln { + enum sip_mthd mthd; + char *req_uri; + char *ver; +}; + +struct sip_stat_ln { + char *ver; + uint32_t status; + char *reason; +}; + +enum sip_strt_type { + REQUEST_LINE, + STATUS_LINE, +}; + +struct sip_strt_ln { + union { + struct sip_reqs_ln *reqs_ln; + struct sip_stat_ln *stat_ln; + }; + enum sip_strt_type type; +}; + +enum sip_mthd sip_mthd_to_enum(char *sip_mthd); +int sip_skip_to_body(char **sip_hdr, size_t *len); +uint32_t sip_body_len(char *sip_hdr); +char * sip_ln_find_crlf(char *sip, size_t len); +size_t sip_ln_len(char *sip, size_t len); +bool sip_ln_cpy_nxt_word(char **sip, size_t *len, char **dst); +bool is_valid_status(uint32_t status); +void sip_ln_skip(char **sdp, size_t *len); + +char * sdp_get_origin(char *orig, size_t len); +char * sdp_get_conn(char *conn, size_t len); +char * sdp_get_media(char *media, size_t len); +struct sip_sdp * +sip_parse_sdp(char *sdp, size_t len); +void free_strt_ln(struct sip_strt_ln *strt_ln); +struct sip_strt_ln * +sip_parse_strt_ln(char *start_line, size_t len); +void free_stat_ln(struct sip_stat_ln *stat_ln); +struct sip_stat_ln * +sip_parse_stat_ln(char *start_ln, size_t len); +void free_reqs_ln(struct sip_reqs_ln *reqs_ln); +struct sip_reqs_ln * +sip_parse_reqs_ln(char *start_ln, size_t len); + +/* Given a pointer to the beginning of the SIP message, return a pointer to + * the beginning of its message-header. + * This modifies the passed 'len' to the right size now that the SIP start-line + * has been discarded */ +static inline void * +sip_msg_hdr(char *sip, size_t *len) { + + char *msg_hdr = sip; + + sip_ln_skip(&msg_hdr, len); + + return msg_hdr; +} + +/* Given a pointer to the beginning of the SIP message, return a pointer to + * the beginning of its message-body. + * This modifies the passed 'len' to the right size now that both the SIP start + * -line and message-header have been discarded */ +static inline void * +sip_msg_bdy(char *sip, size_t *len) { + + char *msg_bdy = sip; + + sip_skip_to_body(&msg_bdy, len); + + return msg_bdy; +}