📄 msg_parser.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@ingroup msg_parser * @CFILE msg_parser.c * * HTTP-like message parser engine. * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * * @date Created: Thu Oct 5 14:01:24 2000 ppessi * *//*#define NDEBUG*/#include "config.h"#include <stddef.h>#include <stdlib.h>#include <string.h>#include <stdio.h>#include <assert.h>#include <limits.h>#include <errno.h>#include <stdarg.h>#include <sofia-sip/su_tagarg.h>#include <sofia-sip/su.h>#include <sofia-sip/su_alloc.h>#include "msg_internal.h"#include "sofia-sip/msg_header.h"#include "sofia-sip/bnf.h"#include "sofia-sip/msg_parser.h"#include "sofia-sip/msg_mclass.h"#include "sofia-sip/msg_mclass_hash.h"#include "sofia-sip/msg_mime.h"#if HAVE_FUNC#elif HAVE_FUNCTION#define __func__ __FUNCTION__#elsestatic char const __func__[] = "msg_parser";#endifstatic int _msg_header_add_dup_as(msg_t *msg, msg_pub_t *pub, msg_hclass_t *hc, msg_header_t const *src);static void msg_insert_chain(msg_t *msg, msg_pub_t *pub, int prepend, msg_header_t **head, msg_header_t *h);static void msg_insert_here_in_chain(msg_t *msg, msg_header_t **prev, msg_header_t *h);static inline msg_header_t *msg_chain_remove(msg_t *msg, msg_header_t *h);#ifndef NDEBUGstatic int msg_chain_loop(msg_header_t const *h);static int msg_chain_errors(msg_header_t const *h);#endif/* ====================================================================== *//* Message properties *//** Get message flags. */unsigned msg_get_flags(msg_t const *msg, unsigned mask){ return msg ? msg->m_object->msg_flags & mask : 0;}/** Set message flags. */unsigned msg_set_flags(msg_t *msg, unsigned mask){ return msg ? msg->m_object->msg_flags |= mask : 0;}/** Clear message flags. */unsigned msg_zap_flags(msg_t *msg, unsigned mask){ return msg ? msg->m_object->msg_flags &= ~mask : 0;}/** Test if streaming is in progress. */int msg_is_streaming(msg_t const *msg){ return msg && msg->m_streaming != 0;}/** Enable/disable streaming */void msg_set_streaming(msg_t *msg, enum msg_streaming_status what){ if (msg) msg->m_streaming = what != 0;}/* ---------------------------------------------------------------------- *//** Test if header is not in the chain */#define msg_header_is_removed(h) ((h)->sh_prev == NULL)static inline int msg_is_request(msg_header_t const *h){ return h->sh_class->hc_hash == msg_request_hash;}static inline int msg_is_status(msg_header_t const *h){ return h->sh_class->hc_hash == msg_status_hash;}/* ====================================================================== *//* Message buffer management *//** Allocate a buffer of @a size octets, with slack of #msg_min_size. */void *msg_buf_alloc(msg_t *msg, unsigned size){ struct msg_mbuffer_s *mb = msg->m_buffer; unsigned room = mb->mb_size - mb->mb_commit - mb->mb_used; int target_size; if (mb->mb_data && room >= (unsigned)size) return mb->mb_data + mb->mb_used + mb->mb_commit; target_size = msg_min_size * ((size + mb->mb_commit) / msg_min_size + 1) - mb->mb_commit; return msg_buf_exact(msg, target_size);}/** Allocate a buffer exactly of @a size octets, without any slack. */void *msg_buf_exact(msg_t *msg, unsigned size){ struct msg_mbuffer_s *mb = msg->m_buffer; unsigned room = mb->mb_size - mb->mb_commit - mb->mb_used; char *buffer; int realloc; if (mb->mb_data && room >= (unsigned)size) return mb->mb_data + mb->mb_used + mb->mb_commit; size += mb->mb_commit; if (msg->m_maxsize && msg->m_size + size > msg->m_maxsize + 1) { msg->m_object->msg_flags |= MSG_FLG_TOOLARGE; errno = msg->m_errno = ENOBUFS; return NULL; } realloc = !mb->mb_used && !msg->m_set_buffer; if (realloc) buffer = su_realloc(msg->m_home, mb->mb_data, size); else buffer = su_alloc(msg->m_home, size); if (!buffer) return NULL; if (!realloc && mb->mb_commit && mb->mb_data) memcpy(buffer, mb->mb_data + mb->mb_used, mb->mb_commit); msg->m_set_buffer = 0; mb->mb_data = buffer; mb->mb_size = size; mb->mb_used = 0; return buffer + mb->mb_commit;}/** Commit data into buffer. */unsigned msg_buf_commit(msg_t *msg, unsigned size, int eos){ if (msg) { struct msg_mbuffer_s *mb = msg->m_buffer; assert(mb->mb_used + mb->mb_commit + size <= mb->mb_size); mb->mb_commit += size; mb->mb_eos = eos; if (mb->mb_used == 0 && !msg->m_chunk && !msg->m_set_buffer) { size_t slack = mb->mb_size - mb->mb_commit; if (eos || slack >= msg_min_size) { /* realloc and cut down buffer */ size_t new_size; void *new_data; if (eos) new_size = mb->mb_commit + 1; else new_size = mb->mb_commit + msg_min_size; new_data = su_realloc(msg->m_home, mb->mb_data, new_size); if (new_data) { mb->mb_data = new_data, mb->mb_size = new_size; } } } } return 0;}/** Get length of committed data */unsigned msg_buf_committed(msg_t const *msg){ if (msg) return msg->m_buffer->mb_commit; else return 0;}/** Get committed data */void *msg_buf_committed_data(msg_t const *msg){ return msg && msg->m_buffer->mb_data ? msg->m_buffer->mb_data + msg->m_buffer->mb_used : NULL;}unsigned msg_buf_size(msg_t const *msg){ assert(msg); if (msg) { struct msg_mbuffer_s const *mb = msg->m_buffer; return mb->mb_size - mb->mb_commit - mb->mb_used; } else return 0;}static inlinevoid msg_buf_used(msg_t *msg, unsigned used){ msg->m_size += used; msg->m_buffer->mb_used += used; if (msg->m_buffer->mb_commit > used) msg->m_buffer->mb_commit -= used; else msg->m_buffer->mb_commit = 0;}/** Set buffer. */void msg_buf_set(msg_t *msg, void *b, unsigned size){ if (msg) { struct msg_mbuffer_s *mb = msg->m_buffer; assert(!msg->m_set_buffer); /* This can be set only once */ mb->mb_data = b; mb->mb_size = size; mb->mb_used = 0; mb->mb_commit = 0; mb->mb_eos = 0; msg->m_set_buffer = 1; }}/** Move unparsed data from src to dst */void *msg_buf_move(msg_t *dst, msg_t const *src){ void *retval; struct msg_mbuffer_s *db = dst->m_buffer; struct msg_mbuffer_s const *sb = src->m_buffer; if (!dst || !src) return NULL; if (sb->mb_eos) retval = msg_buf_exact(dst, sb->mb_commit + 1); else retval = msg_buf_alloc(dst, sb->mb_commit + 1); if (retval == NULL) return NULL; memcpy(retval, sb->mb_data + sb->mb_used, sb->mb_commit); db->mb_commit += sb->mb_commit; db->mb_eos = sb->mb_eos; return retval;}/** Obtain iovec for receiving the data. * * @relates msg_s * * The function msg_recv_iovec() allocates buffers for receiving @a n bytes * of data available from network. It returns the buffers in the I/O vector * @a vec. The @a vec is allocated by the caller, the available length is * given as @a veclen. If the protocol is message-oriented like UDP or SCTP * and the available data ends at message boundary, the caller should set * the @a exact as 1. Otherwise some extra buffer (known as @em slack) is * allocated). * * Currently, the msg_recv_iovec() allocates buffers in at most two blocks, * so the caller should allocate at least two elements for the I/O vector @a * vec. * * @param msg message object [IN] * @param vec I/O vector [OUT] * @param veclen available length of @a vec [IN] * @param n number of available bytes燵IN] * @param exact true if data ends at message boundary [IN] * * @return * The function msg_recv_iovec() returns the length of I/O vector to * receive data, 0 if there are not enough buffers, or -1 upon an error. */int msg_recv_iovec(msg_t *msg, msg_iovec_t vec[], int veclen, unsigned n, int exact){ int i = 0; msg_payload_t *chunk; unsigned len = 0; char *buf; if (n == 0) return 0; if (veclen == 0) vec = NULL; for (chunk = msg->m_chunk; chunk; chunk = MSG_CHUNK_NEXT(chunk)) { buf = MSG_CHUNK_BUFFER(chunk); len = MSG_CHUNK_AVAIL(chunk); if (len == 0) continue; if (!buf) break; if (len > n) len = n; if (vec) vec[i].mv_base = buf, vec[i].mv_len = len; i++; if (len == n) return i; if (i == veclen) vec = NULL; n -= len; } if (!chunk && msg->m_chunk && msg_get_flags(msg, MSG_FLG_FRAGS)) { /* * If the m_chunk is the last fragment for this message, * receive rest of the data to the next message */ if (msg->m_next == NULL) msg->m_next = msg_create(msg->m_class, msg->m_oflags); if (msg->m_next) { msg->m_next->m_maxsize = msg->m_maxsize; msg_addr_copy(msg->m_next, msg); } msg = msg->m_next; if (msg == NULL) return 0; } if (exact) buf = msg_buf_exact(msg, n + 1), len = n; else if (chunk && len > n && !msg_get_flags(msg, MSG_FLG_CHUNKING)) buf = msg_buf_exact(msg, len + 1); else buf = msg_buf_alloc(msg, n + 1), len = msg_buf_size(msg); if (buf == NULL) return -1; if (vec) vec[i].mv_base = buf, vec[i].mv_len = n; if (chunk) { assert(chunk->pl_data == NULL); assert(chunk->pl_common->h_len == 0); chunk->pl_common->h_data = chunk->pl_data = buf; if (len < MSG_CHUNK_AVAIL(chunk)) { msg_header_t *h = (void*)chunk; h->sh_succ = msg_header_alloc(msg_home(msg), h->sh_class, 0); if (!h->sh_succ) return -1; h->sh_succ->sh_prev = &h->sh_succ; chunk->pl_next = (msg_payload_t *)h->sh_succ; chunk->pl_next->pl_len = chunk->pl_len - len; chunk->pl_len = len; } else if (len > MSG_CHUNK_AVAIL(chunk)) { len = MSG_CHUNK_AVAIL(chunk); } msg_buf_used(msg, len); } return i + 1;#if 0 if ((msg->m_ssize || msg->m_stream) /* && msg_get_flags(msg, MSG_FLG_BODY) */) { /* Streaming */ msg_buffer_t *b, *b0; /* Calculate available size of current buffers */ for (b = msg->m_stream, len = 0; b && n > len; b = b->b_next) len += b->b_avail - b->b_size; /* Allocate new buffers */ if (n > len && msg_buf_external(msg, n, 0) < 0) return -1; for (b0 = msg->m_stream; b0; b0 = b0->b_next) if (b0->b_avail != b0->b_size) break; for (b = b0; b && n > 0; i++, b = b->b_next) { len = b->b_size - b->b_avail; len = n < len ? n : len; if (vec && i < veclen) vec[i].mv_base = b->b_data + b->b_avail, vec[i].mv_len = len; else vec = NULL; n -= len; } return i + 1; }#endif}/** Obtain a buffer for receiving data */int msg_recv_buffer(msg_t *msg, void **return_buffer){ void *buffer; if (!msg) return -1; if (return_buffer == NULL) return_buffer = &buffer; if (msg->m_chunk) { msg_payload_t *pl; for (pl = msg->m_chunk; pl; pl = pl->pl_next) { unsigned n = MSG_CHUNK_AVAIL(pl); if (n) { *return_buffer = MSG_CHUNK_BUFFER(pl); return n; } } return 0; } if (msg_get_flags(msg, MSG_FLG_FRAGS)) { /* Message is complete */ return 0; } else if ((*return_buffer = msg_buf_alloc(msg, 2))) { return msg_buf_size(msg) - 1; } else { return -1; }}/**Commit @a n bytes of buffers. * * @relates msg_s * * The function msg_recv_commit() is called after @a n bytes of data has * been received to the message buffers and the parser can extract the * received data. * * @param msg pointer to message object * @param n number of bytes received * @param eos true if stream is complete * * @note The @a eos should be always true for message-based transports. It * should also be true when a stram oin stream-based transport ends, for * instance, when TCP FIN is received. * * @retval 0 when successful * @retval -1 upon an error. */int msg_recv_commit(msg_t *msg, unsigned n, int eos){ msg_payload_t *pl; if (eos) msg->m_buffer->mb_eos = 1; for (pl = msg->m_chunk; pl; pl = pl->pl_next) { unsigned len = MSG_CHUNK_AVAIL(pl); if (n <= len) len = n; pl->pl_common->h_len += len; n -= len; if (n == 0) return 0; } if (msg->m_chunk && msg->m_next) msg = msg->m_next; return msg_buf_commit(msg, n, eos);}/**Get a next message of the stream. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -