Coverage Report

Created: 2026-07-16 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensips/net/proto_tcp/tcp_common.h
Line
Count
Source
1
/*
2
 * Copyright (C) 2015 - OpenSIPS Foundation
3
 * Copyright (C) 2001-2003 FhG Fokus
4
 *
5
 * This file is part of opensips, a free SIP server.
6
 *
7
 * opensips is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version
11
 *
12
 * opensips is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
20
 *
21
 *
22
 * History:
23
 * -------
24
 *  2015-02-16  split from proto_tcp.c (razvanc)
25
 */
26
27
#ifndef _NET_tcp_common_h
28
#define _NET_tcp_common_h
29
30
#include "../../pt.h"
31
#include "../../receive.h"
32
33
#include "tcp_common_defs.h"
34
35
/*! \brief
36
 * reads all headers (until double crlf), & parses the content-length header
37
 *
38
 * \note (WARNING: inefficient, tries to reuse receive_msg but will go through
39
 * the headers twice [once here looking for Content-Length and for the end
40
 * of the headers and once in receive_msg]; a more speed efficient version will
41
 * result in either major code duplication or major changes to the receive code)
42
 *
43
 * \return nothing and sets r->state & r->body
44
 * when either r->body!=0 or r->state==H_BODY =>
45
 * all headers have been read. It should be called in a while loop */
46
inline static void tcp_parse_headers(struct tcp_req *r,
47
                  int _crlf_pingpong, int _crlf_drop)
48
0
{
49
0
  unsigned int remaining;
50
0
  char *p;
51
52
0
  #define crlf_default_skip_case \
53
0
          case '\n': \
54
0
            r->state=H_LF; \
55
0
            break; \
56
0
          default: \
57
0
            r->state=H_SKIP
58
59
0
  #define content_len_beg_case \
60
0
          case ' ': \
61
0
          case '\t': \
62
0
            if (!r->has_content_len) r->state=H_STARTWS; \
63
0
            else r->state=H_SKIP; \
64
              /* not interested if we already found one */ \
65
0
            break; \
66
0
          case 'C': \
67
0
          case 'c': \
68
0
            if(!r->has_content_len) r->state=H_CONT_LEN1; \
69
0
            else r->state=H_SKIP; \
70
0
            break; \
71
0
          case 'l': \
72
0
          case 'L': \
73
            /* short form for Content-Length */ \
74
0
            if (!r->has_content_len) r->state=H_L_COLON; \
75
0
            else r->state=H_SKIP; \
76
0
            break
77
78
0
  #define change_state(upper, lower, newstate)\
79
0
          switch(*p){ \
80
0
            case upper: \
81
0
            case lower: \
82
0
              r->state=(newstate); break; \
83
0
            crlf_default_skip_case; \
84
0
          }
85
86
0
  #define change_state_case(state0, upper, lower, newstate)\
87
0
          case state0: \
88
0
                change_state(upper, lower, newstate); \
89
0
                p++; \
90
0
                break
91
92
0
  p=r->parsed;
93
94
0
  while(p<r->pos && r->error==TCP_REQ_OK){
95
0
    switch((unsigned char)r->state){
96
0
      case H_BODY: /* read the body*/
97
0
        remaining=r->pos-p;
98
0
        if (remaining>r->bytes_to_go) remaining=r->bytes_to_go;
99
0
        r->bytes_to_go-=remaining;
100
0
        p+=remaining;
101
0
        if (r->bytes_to_go==0){
102
0
          r->complete=1;
103
0
          goto skip;
104
0
        }
105
0
        break;
106
107
0
      case H_SKIP:
108
        /* find lf, we are in this state if we are not interested
109
         * in anything till end of line*/
110
0
        p=q_memchr(p, '\n', r->pos-p);
111
0
        if (p){
112
0
          p++;
113
0
          r->state=H_LF;
114
0
        }else{
115
0
          p=r->pos;
116
0
        }
117
0
        break;
118
119
0
      case H_LF:
120
        /* terminate on LF CR LF or LF LF */
121
0
        switch (*p){
122
0
          case '\r':
123
0
            r->state=H_LFCR;
124
0
            break;
125
0
          case '\n':
126
            /* found LF LF */
127
0
            r->state=H_BODY;
128
0
            if (r->has_content_len){
129
0
              r->body=p+1;
130
0
              r->bytes_to_go=r->content_len;
131
0
              if (r->bytes_to_go==0){
132
0
                r->complete=1;
133
0
                p++;
134
0
                goto skip;
135
0
              }
136
0
            }else{
137
0
              LM_DBG("no clen, p=%X\n", *p);
138
0
              r->error=TCP_REQ_BAD_LEN;
139
0
            }
140
0
            break;
141
0
          content_len_beg_case;
142
0
          default:
143
0
            r->state=H_SKIP;
144
0
        }
145
0
        p++;
146
0
        break;
147
0
      case H_LFCR:
148
0
        if (*p=='\n'){
149
          /* found LF CR LF */
150
0
          r->state=H_BODY;
151
0
          if (r->has_content_len){
152
0
            r->body=p+1;
153
0
            r->bytes_to_go=r->content_len;
154
0
            if (r->bytes_to_go==0){
155
0
              r->complete=1;
156
0
              p++;
157
0
              goto skip;
158
0
            }
159
0
          }else{
160
0
            LM_DBG("no clen, p=%X\n", *p);
161
0
            r->error=TCP_REQ_BAD_LEN;
162
0
          }
163
0
        }else r->state=H_SKIP;
164
0
        p++;
165
0
        break;
166
167
0
      case H_STARTWS:
168
0
        switch (*p){
169
0
          content_len_beg_case;
170
0
          crlf_default_skip_case;
171
0
        }
172
0
        p++;
173
0
        break;
174
0
      case H_SKIP_EMPTY:
175
0
        switch (*p){
176
0
          case '\n':
177
0
            break;
178
0
          case '\r':
179
0
            if (_crlf_pingpong) {
180
0
              r->state=H_SKIP_EMPTY_CR_FOUND;
181
0
              r->start=p;
182
0
            }
183
0
            break;
184
0
          case ' ':
185
0
          case '\t':
186
            /* skip empty lines */
187
0
            break;
188
0
          case 'C':
189
0
          case 'c':
190
0
            r->state=H_CONT_LEN1;
191
0
            r->start=p;
192
0
            break;
193
0
          case 'l':
194
0
          case 'L':
195
            /* short form for Content-Length */
196
0
            r->state=H_L_COLON;
197
0
            r->start=p;
198
0
            break;
199
0
          default:
200
0
            r->state=H_SKIP;
201
0
            r->start=p;
202
0
        };
203
0
        p++;
204
0
        break;
205
0
      case H_SKIP_EMPTY_CR_FOUND:
206
0
        if (*p=='\n'){
207
0
          r->state=H_SKIP_EMPTY_CRLF_FOUND;
208
0
          p++;
209
0
        }else{
210
0
          r->state=H_SKIP_EMPTY;
211
0
        }
212
0
        break;
213
214
0
      case H_SKIP_EMPTY_CRLF_FOUND:
215
0
        if (*p=='\r'){
216
0
          r->state = H_SKIP_EMPTY_CRLFCR_FOUND;
217
0
          p++;
218
0
        }else{
219
0
          r->state = H_SKIP_EMPTY;
220
0
        }
221
0
        break;
222
223
0
      case H_SKIP_EMPTY_CRLFCR_FOUND:
224
0
        if (*p=='\n'){
225
0
          r->state = H_PING_CRLFCRLF;
226
0
          r->complete = 1;
227
0
          r->has_content_len = 1; /* hack to avoid error check */
228
0
          p++;
229
0
          goto skip;
230
0
        }else{
231
0
          r->state = H_SKIP_EMPTY;
232
0
        }
233
0
        break;
234
0
      change_state_case(H_CONT_LEN1,  'O', 'o', H_CONT_LEN2);
235
0
      change_state_case(H_CONT_LEN2,  'N', 'n', H_CONT_LEN3);
236
0
      change_state_case(H_CONT_LEN3,  'T', 't', H_CONT_LEN4);
237
0
      change_state_case(H_CONT_LEN4,  'E', 'e', H_CONT_LEN5);
238
0
      change_state_case(H_CONT_LEN5,  'N', 'n', H_CONT_LEN6);
239
0
      change_state_case(H_CONT_LEN6,  'T', 't', H_CONT_LEN7);
240
0
      change_state_case(H_CONT_LEN7,  '-', '_', H_CONT_LEN8);
241
0
      change_state_case(H_CONT_LEN8,  'L', 'l', H_CONT_LEN9);
242
0
      change_state_case(H_CONT_LEN9,  'E', 'e', H_CONT_LEN10);
243
0
      change_state_case(H_CONT_LEN10, 'N', 'n', H_CONT_LEN11);
244
0
      change_state_case(H_CONT_LEN11, 'G', 'g', H_CONT_LEN12);
245
0
      change_state_case(H_CONT_LEN12, 'T', 't', H_CONT_LEN13);
246
0
      change_state_case(H_CONT_LEN13, 'H', 'h', H_L_COLON);
247
248
0
      case H_L_COLON:
249
0
        switch(*p){
250
0
          case ' ':
251
0
          case '\t':
252
0
            break; /* skip space */
253
0
          case ':':
254
0
            r->state=H_CONT_LEN_BODY;
255
0
            break;
256
0
          crlf_default_skip_case;
257
0
        };
258
0
        p++;
259
0
        break;
260
261
0
      case  H_CONT_LEN_BODY:
262
0
        switch(*p){
263
0
          case ' ':
264
0
          case '\t':
265
0
            break; /* eat space */
266
0
          case '0':
267
0
          case '1':
268
0
          case '2':
269
0
          case '3':
270
0
          case '4':
271
0
          case '5':
272
0
          case '6':
273
0
          case '7':
274
0
          case '8':
275
0
          case '9':
276
0
            r->state=H_CONT_LEN_BODY_PARSE;
277
0
            r->content_len=(*p-'0');
278
0
            break;
279
          /*FIXME: content length on different lines ! */
280
0
          crlf_default_skip_case;
281
0
        }
282
0
        p++;
283
0
        break;
284
285
0
      case H_CONT_LEN_BODY_PARSE:
286
0
        switch(*p){
287
0
          case '0':
288
0
          case '1':
289
0
          case '2':
290
0
          case '3':
291
0
          case '4':
292
0
          case '5':
293
0
          case '6':
294
0
          case '7':
295
0
          case '8':
296
0
          case '9':
297
0
            if (r->content_len>=TCP_BUF_SIZE) {
298
0
              LM_ERR("Content-Length value %d bigger than the "
299
0
                "reading buffer\n", r->content_len);
300
0
              r->error = TCP_REQ_BAD_LEN;
301
0
              r->state = H_SKIP;
302
0
              r->content_len = 0;
303
0
              break;
304
0
            }
305
0
            r->content_len=r->content_len*10+(*p-'0');
306
0
            if (r->content_len>=TCP_BUF_SIZE) {
307
0
              LM_ERR("Content-Length value %d bigger than the "
308
0
                "reading buffer\n", r->content_len);
309
0
              r->error = TCP_REQ_BAD_LEN;
310
0
              r->state = H_SKIP;
311
0
              r->content_len = 0;
312
0
            }
313
0
            break;
314
0
          case '\r':
315
0
          case ' ':
316
0
          case '\t': /* FIXME: check if line contains only WS */
317
0
            r->state=H_SKIP;
318
0
            r->has_content_len=1;
319
0
            break;
320
0
          case '\n':
321
            /* end of line, parse successful */
322
0
            r->state=H_LF;
323
0
            r->has_content_len=1;
324
0
            break;
325
0
          default:
326
0
            LM_ERR("bad Content-Length header value, unexpected "
327
0
                "char %c in state %d\n", *p, r->state);
328
0
            r->state=H_SKIP; /* try to find another?*/
329
0
        }
330
0
        p++;
331
0
        break;
332
333
0
      default:
334
0
        LM_CRIT("unexpected state %d\n", r->state);
335
#ifdef USE_ABORT
336
        abort();
337
#endif
338
0
    }
339
0
  }
340
0
  if (r->state == H_SKIP_EMPTY_CRLF_FOUND && _crlf_drop) {
341
0
    r->state = H_SKIP_EMPTY;
342
0
    r->complete = 1;
343
0
    r->has_content_len = 1; /* hack to avoid error check */
344
0
  }
345
0
skip:
346
0
  r->parsed=p;
347
0
  return;
348
0
}
349
350
351
352
static inline int tcp_handle_req(struct tcp_req *req,
353
    struct tcp_connection *con, int _max_msg_chunks)
354
0
{
355
0
  struct receive_info local_rcv;
356
0
  char *msg_buf;
357
0
  int msg_len;
358
0
  long size;
359
0
  char c;
360
361
0
  if (req->complete){
362
#ifdef EXTRA_DEBUG
363
    LM_DBG("end of header part\n");
364
    LM_DBG("- received from: port %d\n", con->rcv.src_port);
365
    print_ip("- received from: ip ", &con->rcv.src_ip, "\n");
366
    LM_DBG("headers:\n%.*s.\n",(int)(req->body-req->start), req->start);
367
#endif
368
0
    if (req->has_content_len){
369
0
      LM_DBG("content-length= %d\n", req->content_len);
370
#ifdef EXTRA_DEBUG
371
      LM_DBG("body:\n%.*s\n", req->content_len,req->body);
372
#endif
373
0
    }else{
374
0
      req->error=TCP_REQ_BAD_LEN;
375
0
      LM_ERR("content length not present or unparsable\n");
376
0
      goto error;
377
0
    }
378
379
    /* refresh connection lifetime after successful read progress */
380
0
    tcp_conn_reset_lifetime(con);
381
0
    con->timeout=con->lifetime;
382
383
    /* if we are here everything is nice and ok*/
384
#ifdef EXTRA_DEBUG
385
    LM_DBG("calling receive_msg(%p, %d, )\n",
386
        req->start, (int)(req->parsed-req->start));
387
#endif
388
    /* rcv.bind_address should always be !=0 */
389
0
    bind_address=con->rcv.bind_address;
390
    /* just for debugging use sendipv4 as receiving socket  FIXME*/
391
0
    con->rcv.proto_reserved1=con->id; /* copy the id */
392
0
    c=*req->parsed; /* ugly hack: zero term the msg & save the
393
               previous char, req->parsed should be ok
394
               because we always alloc BUF_SIZE+1 */
395
0
    *req->parsed=0;
396
397
    /* prepare for next request */
398
0
    size=req->pos-req->parsed;
399
0
    con->msg_attempts = 0;
400
401
0
    if (req->state==H_PING_CRLFCRLF) {
402
      /* we send the reply */
403
0
      if (_tcp_common_write( con, con->fd, CRLF, CRLF_LEN) < 0) {
404
0
        LM_ERR("CRLF pong - _tcp_common_write() failed\n");
405
0
      }
406
0
    } else if (req->state!=H_SKIP_EMPTY) {
407
0
      msg_buf = req->start;
408
0
      msg_len = req->parsed-req->start;
409
0
      local_rcv = con->rcv;
410
411
0
      if (!size) {
412
0
        LM_DBG("Nothing more to read on TCP conn %p, currently in state %d \n",
413
0
          con,con->state);
414
0
      } else {
415
0
        LM_DBG("We still have things on the pipe - "
416
0
          "keeping connection \n");
417
0
      }
418
419
0
      if (tcp_dispatch_msg(msg_buf, msg_len,
420
0
          &local_rcv, NULL, 0) < 0) {
421
0
        LM_ERR("failed to deliver TCP message\n");
422
0
        goto error;
423
0
      }
424
0
    }
425
426
0
    if (size) {
427
      /* restoring the char only makes sense if there is something else to
428
       * process, otherwise we can leave it. This prevents us from accessing
429
       * unallocated memory - razvanc */
430
0
      *req->parsed=c;
431
0
      memmove(req->buf, req->parsed, size);
432
433
#ifdef EXTRA_DEBUG
434
      LM_DBG("preparing for new request, kept %ld bytes\n", size);
435
#endif
436
0
      init_tcp_req(req, size);
437
438
      /* if we still have some unparsed bytes, try to parse them too */
439
0
      return 1;
440
0
    }
441
442
0
  } else {
443
    /* request not complete - check the if the thresholds are exceeded */
444
0
    if (con->msg_attempts == 0)
445
0
      tcp_conn_set_msg_read_timeout(con);
446
447
0
    con->msg_attempts ++;
448
0
    if (con->msg_attempts == _max_msg_chunks) {
449
0
      LM_ERR("Made %u read attempts but message is not complete yet - "
450
0
           "closing connection \n",con->msg_attempts);
451
0
      goto error;
452
0
    }
453
0
  }
454
455
0
  return 0;
456
0
error:
457
  /* report error */
458
0
  return -1;
459
0
}
460
461
0
#define TRANS_TRACE_PROTO_ID "net"
462
463
#endif