Coverage Report

Created: 2026-03-11 06:49

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
            r->content_len=r->content_len*10+(*p-'0');
298
0
            if (r->content_len>=TCP_BUF_SIZE) {
299
0
              LM_ERR("Content-Length value %d bigger than the "
300
0
                "reading buffer\n", r->content_len);
301
0
              r->error = TCP_REQ_BAD_LEN;
302
0
              r->state = H_SKIP;
303
0
              r->content_len = 0;
304
0
            }
305
0
            break;
306
0
          case '\r':
307
0
          case ' ':
308
0
          case '\t': /* FIXME: check if line contains only WS */
309
0
            r->state=H_SKIP;
310
0
            r->has_content_len=1;
311
0
            break;
312
0
          case '\n':
313
            /* end of line, parse successful */
314
0
            r->state=H_LF;
315
0
            r->has_content_len=1;
316
0
            break;
317
0
          default:
318
0
            LM_ERR("bad Content-Length header value, unexpected "
319
0
                "char %c in state %d\n", *p, r->state);
320
0
            r->state=H_SKIP; /* try to find another?*/
321
0
        }
322
0
        p++;
323
0
        break;
324
325
0
      default:
326
0
        LM_CRIT("unexpected state %d\n", r->state);
327
#ifdef USE_ABORT
328
        abort();
329
#endif
330
0
    }
331
0
  }
332
0
  if (r->state == H_SKIP_EMPTY_CRLF_FOUND && _crlf_drop) {
333
0
    r->state = H_SKIP_EMPTY;
334
0
    r->complete = 1;
335
0
    r->has_content_len = 1; /* hack to avoid error check */
336
0
  }
337
0
skip:
338
0
  r->parsed=p;
339
0
  return;
340
0
}
341
342
343
344
static inline int tcp_handle_req(struct tcp_req *req,
345
    struct tcp_connection *con, int _max_msg_chunks,
346
    int _parallel_handling)
347
0
{
348
0
  struct receive_info local_rcv;
349
0
  char *msg_buf;
350
0
  int msg_len;
351
0
  long size;
352
0
  char c, *msg_buf_cpy = NULL;
353
354
0
  if (req->complete){
355
#ifdef EXTRA_DEBUG
356
    LM_DBG("end of header part\n");
357
    LM_DBG("- received from: port %d\n", con->rcv.src_port);
358
    print_ip("- received from: ip ", &con->rcv.src_ip, "\n");
359
    LM_DBG("headers:\n%.*s.\n",(int)(req->body-req->start), req->start);
360
#endif
361
0
    if (req->has_content_len){
362
0
      LM_DBG("content-length= %d\n", req->content_len);
363
#ifdef EXTRA_DEBUG
364
      LM_DBG("body:\n%.*s\n", req->content_len,req->body);
365
#endif
366
0
    }else{
367
0
      req->error=TCP_REQ_BAD_LEN;
368
0
      LM_ERR("content length not present or unparsable\n");
369
0
      goto error;
370
0
    }
371
372
    /* update the timeout - we successfully read the request */
373
0
    tcp_conn_reset_lifetime(con);
374
0
    con->timeout=con->lifetime;
375
376
    /* if we are here everything is nice and ok*/
377
#ifdef EXTRA_DEBUG
378
    LM_DBG("calling receive_msg(%p, %d, )\n",
379
        req->start, (int)(req->parsed-req->start));
380
#endif
381
    /* rcv.bind_address should always be !=0 */
382
0
    bind_address=con->rcv.bind_address;
383
    /* just for debugging use sendipv4 as receiving socket  FIXME*/
384
0
    con->rcv.proto_reserved1=con->id; /* copy the id */
385
0
    c=*req->parsed; /* ugly hack: zero term the msg & save the
386
               previous char, req->parsed should be ok
387
               because we always alloc BUF_SIZE+1 */
388
0
    *req->parsed=0;
389
390
    /* prepare for next request */
391
0
    size=req->pos-req->parsed;
392
0
    con->msg_attempts = 0;
393
394
0
    if (req->state==H_PING_CRLFCRLF) {
395
      /* we send the reply */
396
0
      if (_tcp_common_write( con, con->fd, CRLF, CRLF_LEN) < 0) {
397
0
        LM_ERR("CRLF pong - _tcp_common_write() failed\n");
398
0
      }
399
0
    } else if (req->state!=H_SKIP_EMPTY) {
400
0
      msg_buf = req->start;
401
0
      msg_len = req->parsed-req->start;
402
0
      local_rcv = con->rcv;
403
404
0
      if (!size) {
405
        /* did not read any more things -  we can release
406
         * the connection */
407
0
        LM_DBG("Nothing more to read on TCP conn %p, currently in state %d \n",
408
0
          con,con->state);
409
0
        if (req != &_tcp_common_current_req) {
410
          /* we have the buffer in the connection tied buff -
411
           *  detach it , release the conn and free it afterwards */
412
0
          con->con_req = NULL;
413
0
        }
414
415
        /* If parallel handling, make a copy (null terminted) of the
416
         * current reading buffer (so we can continue its handling)
417
         * and release the TCP conn on READ */
418
0
        if ( (_parallel_handling!=0) &&
419
0
        (msg_buf_cpy=(char*)pkg_malloc( msg_len+1 )) !=NULL ) {
420
0
          memcpy( msg_buf_cpy, msg_buf, msg_len);
421
0
          msg_buf_cpy[msg_len] = 0;
422
0
          msg_buf = msg_buf_cpy;
423
0
          tcp_done_reading( con );
424
0
          con = NULL; /* having reached this, we MUST return 2 */
425
0
        }
426
427
0
      } else {
428
0
        LM_DBG("We still have things on the pipe - "
429
0
          "keeping connection \n");
430
0
      }
431
432
0
      if (receive_msg(msg_buf, msg_len,
433
0
        &local_rcv, NULL, 0) <0)
434
0
          LM_ERR("receive_msg failed \n");
435
436
0
      if (msg_buf_cpy)
437
0
        pkg_free(msg_buf_cpy);
438
0
    }
439
440
0
    if (size) {
441
      /* restoring the char only makes sense if there is something else to
442
       * process, otherwise we can leave it. This prevents us from accessing
443
       * unallocated memory - razvanc */
444
0
      *req->parsed=c;
445
0
      memmove(req->buf, req->parsed, size);
446
447
#ifdef EXTRA_DEBUG
448
      LM_DBG("preparing for new request, kept %ld bytes\n", size);
449
#endif
450
0
      init_tcp_req(req, size);
451
452
      /* if we still have some unparsed bytes, try to parse them too */
453
0
      return 1;
454
0
    }
455
456
0
    if (req != &_tcp_common_current_req) {
457
      /* if we no longer need this tcp_req
458
       * we can free it now */
459
0
      shm_free(req);
460
0
      if (con)
461
0
        con->con_req = NULL;
462
0
    }
463
0
  } else {
464
    /* request not complete - check the if the thresholds are exceeded */
465
0
    if (con->msg_attempts==0)
466
      /* if first iteration, set a short timeout for reading
467
       * a whole SIP message */
468
0
      con->timeout = get_ticks() + con->profile.msg_read_timeout;
469
470
0
    con->msg_attempts ++;
471
0
    if (con->msg_attempts == _max_msg_chunks) {
472
0
      LM_ERR("Made %u read attempts but message is not complete yet - "
473
0
           "closing connection \n",con->msg_attempts);
474
0
      goto error;
475
0
    }
476
477
0
    if (req == &_tcp_common_current_req) {
478
      /* let's duplicate this - most likely another conn will come in */
479
480
0
      LM_DBG("We didn't manage to read a full request on con %p\n",con);
481
0
      con->con_req = shm_malloc(sizeof(struct tcp_req));
482
0
      if (con->con_req == NULL) {
483
0
        LM_ERR("No more mem for dynamic con request buffer\n");
484
0
        goto error;
485
0
      }
486
487
0
      if (req->pos != req->buf) {
488
        /* we have read some bytes */
489
0
        memcpy(con->con_req->buf,req->buf,req->pos-req->buf);
490
0
        con->con_req->pos = con->con_req->buf + (req->pos-req->buf);
491
0
      } else {
492
0
        con->con_req->pos = con->con_req->buf;
493
0
      }
494
495
0
      if (req->start != req->buf)
496
0
        con->con_req->start = con->con_req->buf +(req->start-req->buf);
497
0
      else
498
0
        con->con_req->start = con->con_req->buf;
499
500
0
      if (req->parsed != req->buf)
501
0
        con->con_req->parsed =con->con_req->buf+(req->parsed-req->buf);
502
0
      else
503
0
        con->con_req->parsed = con->con_req->buf;
504
505
0
      if (req->body != 0) {
506
0
        con->con_req->body = con->con_req->buf + (req->body-req->buf);
507
0
      } else
508
0
        con->con_req->body = 0;
509
510
0
      con->con_req->complete=req->complete;
511
0
      con->con_req->has_content_len=req->has_content_len;
512
0
      con->con_req->content_len=req->content_len;
513
0
      con->con_req->bytes_to_go=req->bytes_to_go;
514
0
      con->con_req->error = req->error;
515
0
      con->con_req->state = req->state;
516
      /* req will be reset on the next usage */
517
0
    }
518
0
  }
519
520
  /* everything ok; if connection was returned already, use special rc 2 */
521
0
  return con ? 0 : 2;
522
0
error:
523
  /* report error */
524
0
  return -1;
525
0
}
526
527
0
#define TRANS_TRACE_PROTO_ID "net"
528
529
#endif
530