static int ws_process_msg(char* tcpbuf, unsigned int len, struct receive_info* rcv_info, struct tcp_connection* con) { int ret; tcp_event_info_t tev; ret = 0; LM_DBG("WebSocket Message: [[>>>\n%.*s<<<]]\n", len, tcpbuf); if(likely(sr_event_enabled(SREV_TCP_WS_FRAME_IN))) { memset(&tev, 0, sizeof(tcp_event_info_t)); tev.type = SREV_TCP_WS_FRAME_IN; tev.buf = tcpbuf; tev.len = len; tev.rcv = rcv_info; tev.con = con; ret = sr_event_exec(SREV_TCP_WS_FRAME_IN, (void*)(&tev)); } else { LM_DBG("no callback registering for handling WebSockets - dropping!\n"); } return ret; }
static int mod_init(void) { if (sl_load_api(&ws_slb) != 0) { LM_ERR("binding to SL\n"); goto error; } if (sr_event_register_cb(SREV_TCP_WS_FRAME_IN, ws_frame_receive) != 0) { LM_ERR("registering WebSocket receive call-back\n"); goto error; } if (sr_event_register_cb(SREV_TCP_WS_FRAME_OUT, ws_frame_transmit) != 0) { LM_ERR("registering WebSocket transmit call-back\n"); goto error; } if (register_module_stats(exports.name, stats) != 0) { LM_ERR("registering core statistics\n"); goto error; } if (register_mi_mod(exports.name, mi_cmds) != 0) { LM_ERR("registering MI commands\n"); goto error; } if (wsconn_init() < 0) { LM_ERR("initialising WebSocket connections table\n"); goto error; } if (ws_ping_application_data.len < 1 || ws_ping_application_data.len > 125) { ws_ping_application_data.s = DEFAULT_PING_APPLICATION_DATA + 8; ws_ping_application_data.len = DEFAULT_PING_APPLICATION_DATA_LEN - 8; } if (ws_keepalive_mechanism != KEEPALIVE_MECHANISM_NONE) { if (ws_keepalive_timeout < 1 || ws_keepalive_timeout > 3600) ws_keepalive_timeout = DEFAULT_KEEPALIVE_TIMEOUT; switch(ws_keepalive_mechanism) { case KEEPALIVE_MECHANISM_PING: case KEEPALIVE_MECHANISM_PONG: break; default: ws_keepalive_mechanism = DEFAULT_KEEPALIVE_MECHANISM; break; } if (ws_keepalive_interval < 1 || ws_keepalive_interval > 60) ws_keepalive_interval = DEFAULT_KEEPALIVE_INTERVAL; if (ws_keepalive_processes < 1 || ws_keepalive_processes > 16) ws_keepalive_processes = DEFAULT_KEEPALIVE_PROCESSES; /* Add extra process/timer for the keepalive process */ register_sync_timers(ws_keepalive_processes); } if (ws_sub_protocols & SUB_PROTOCOL_MSRP && !sr_event_enabled(SREV_TCP_MSRP_FRAME)) ws_sub_protocols &= ~SUB_PROTOCOL_MSRP; if ((ws_sub_protocols & SUB_PROTOCOL_ALL) == 0) { LM_ERR("no sub-protocols enabled\n"); goto error; } if ((ws_sub_protocols | SUB_PROTOCOL_ALL) != SUB_PROTOCOL_ALL) { LM_ERR("unrecognised sub-protocols enabled\n"); goto error; } if (ws_cors_mode < 0 || ws_cors_mode > 2) { LM_ERR("bad value for cors_mode\n"); goto error; } if (cfg_declare("websocket", ws_cfg_def, &default_ws_cfg, cfg_sizeof(websocket), &ws_cfg)) { LM_ERR("declaring configuration\n"); return -1; } cfg_get(websocket, ws_cfg, keepalive_timeout) = ws_keepalive_timeout; if (!module_loaded("xhttp")) { LM_ERR("\"xhttp\" must be loaded to use WebSocket.\n"); return -1; } if (((ws_sub_protocols & SUB_PROTOCOL_SIP) == SUB_PROTOCOL_SIP) && !module_loaded("nathelper") && !module_loaded("outbound")) { LM_WARN("neither \"nathelper\" nor \"outbound\" modules are" " loaded. At least one of these is required for correct" " routing of SIP over WebSocket.\n"); } return 0; error: wsconn_destroy(); return -1; }
/** Receive message * WARNING: buf must be 0 terminated (buf[len]=0) or some things might * break (e.g.: modules/textops) */ int receive_msg(char* buf, unsigned int len, struct receive_info* rcv_info) { struct sip_msg* msg; struct run_act_ctx ctx; int ret; #ifdef STATS int skipped = 1; struct timeval tvb, tve; struct timezone tz; unsigned int diff; #endif str inb; sr_net_info_t netinfo; if(sr_event_enabled(SREV_NET_DATA_RECV)) { if(sip_check_fline(buf, len)==0) { memset(&netinfo, 0, sizeof(sr_net_info_t)); netinfo.data.s = buf; netinfo.data.len = len; netinfo.rcv = rcv_info; sr_event_exec(SREV_NET_DATA_RECV, (void*)&netinfo); } } inb.s = buf; inb.len = len; sr_event_exec(SREV_NET_DATA_IN, (void*)&inb); len = inb.len; msg=pkg_malloc(sizeof(struct sip_msg)); if (msg==0) { LM_ERR("no mem for sip_msg\n"); goto error00; } msg_no++; /* number of vias parsed -- good for diagnostic info in replies */ via_cnt=0; memset(msg,0, sizeof(struct sip_msg)); /* init everything to 0 */ /* fill in msg */ msg->buf=buf; msg->len=len; /* zero termination (termination of orig message bellow not that * useful as most of the work is done with scratch-pad; -jiri */ /* buf[len]=0; */ /* WARNING: zero term removed! */ msg->rcv=*rcv_info; msg->id=msg_no; msg->pid=my_pid(); msg->set_global_address=default_global_address; msg->set_global_port=default_global_port; if(likely(sr_msg_time==1)) msg_set_time(msg); if (parse_msg(buf,len, msg)!=0){ if(sr_event_exec(SREV_RCV_NOSIP, (void*)msg)!=0) { LOG(cfg_get(core, core_cfg, corelog), "core parsing of SIP message failed (%s:%d/%d)\n", ip_addr2a(&msg->rcv.src_ip), (int)msg->rcv.src_port, (int)msg->rcv.proto); sr_core_ert_run(msg, SR_CORE_ERT_RECEIVE_PARSE_ERROR); } goto error02; } LM_DBG("After parse_msg...\n"); /* set log prefix */ log_prefix_set(msg); /* ... clear branches from previous message */ clear_branches(); if (msg->first_line.type==SIP_REQUEST){ ruri_mark_new(); /* ruri is usable for forking (not consumed yet) */ if (!IS_SIP(msg)){ if ((ret=nonsip_msg_run_hooks(msg))!=NONSIP_MSG_ACCEPT){ if (unlikely(ret==NONSIP_MSG_ERROR)) goto error03; goto end; /* drop the message */ } } /* sanity checks */ if ((msg->via1==0) || (msg->via1->error!=PARSE_OK)){ /* no via, send back error ? */ LM_ERR("no via found in request\n"); STATS_BAD_MSG(); goto error02; } /* check if necessary to add receive?->moved to forward_req */ /* check for the alias stuff */ #ifdef USE_TCP if (msg->via1->alias && cfg_get(tcp, tcp_cfg, accept_aliases) && (((rcv_info->proto==PROTO_TCP) && !tcp_disable) #ifdef USE_TLS || ((rcv_info->proto==PROTO_TLS) && !tls_disable) #endif ) ){ if (tcpconn_add_alias(rcv_info->proto_reserved1, msg->via1->port, rcv_info->proto)!=0){ LM_ERR("tcp alias failed\n"); /* continue */ } } #endif /* skip: */ LM_DBG("preparing to run routing scripts...\n"); #ifdef STATS gettimeofday( & tvb, &tz ); #endif /* execute pre-script callbacks, if any; -jiri */ /* if some of the callbacks said not to continue with * script processing, don't do so * if we are here basic sanity checks are already done * (like presence of at least one via), so you can count * on via1 being parsed in a pre-script callback --andrei */ if (exec_pre_script_cb(msg, REQUEST_CB_TYPE)==0 ) { STATS_REQ_FWD_DROP(); goto end; /* drop the request */ } set_route_type(REQUEST_ROUTE); /* exec the routing script */ if (run_top_route(main_rt.rlist[DEFAULT_RT], msg, 0)<0){ LM_WARN("error while trying script\n"); goto error_req; } #ifdef STATS gettimeofday( & tve, &tz ); diff = (tve.tv_sec-tvb.tv_sec)*1000000+(tve.tv_usec-tvb.tv_usec); stats->processed_requests++; stats->acc_req_time += diff; LM_DBG("successfully ran routing scripts...(%d usec)\n", diff); STATS_RX_REQUEST( msg->first_line.u.request.method_value ); #endif /* execute post request-script callbacks */ exec_post_script_cb(msg, REQUEST_CB_TYPE); }else if (msg->first_line.type==SIP_REPLY){ /* sanity checks */ if ((msg->via1==0) || (msg->via1->error!=PARSE_OK)){ /* no via, send back error ? */ LM_ERR("no via found in reply\n"); STATS_BAD_RPL(); goto error02; } #ifdef STATS gettimeofday( & tvb, &tz ); STATS_RX_RESPONSE ( msg->first_line.u.reply.statuscode / 100 ); #endif /* execute pre-script callbacks, if any; -jiri */ /* if some of the callbacks said not to continue with * script processing, don't do so * if we are here basic sanity checks are already done * (like presence of at least one via), so you can count * on via1 being parsed in a pre-script callback --andrei */ if (exec_pre_script_cb(msg, ONREPLY_CB_TYPE)==0 ) { STATS_RPL_FWD_DROP(); goto end; /* drop the reply */ } /* exec the onreply routing script */ if (onreply_rt.rlist[DEFAULT_RT]){ set_route_type(CORE_ONREPLY_ROUTE); ret=run_top_route(onreply_rt.rlist[DEFAULT_RT], msg, &ctx); #ifndef NO_ONREPLY_ROUTE_ERROR if (unlikely(ret<0)){ LM_WARN("error while trying onreply script\n"); goto error_rpl; }else #endif /* NO_ONREPLY_ROUTE_ERROR */ if (unlikely(ret==0 || (ctx.run_flags&DROP_R_F))){ STATS_RPL_FWD_DROP(); goto skip_send_reply; /* drop the message, no error */ } } /* send the msg */ forward_reply(msg); skip_send_reply: #ifdef STATS gettimeofday( & tve, &tz ); diff = (tve.tv_sec-tvb.tv_sec)*1000000+(tve.tv_usec-tvb.tv_usec); stats->processed_responses++; stats->acc_res_time+=diff; LM_DBG("successfully ran reply processing...(%d usec)\n", diff); #endif /* execute post reply-script callbacks */ exec_post_script_cb(msg, ONREPLY_CB_TYPE); } end: #ifdef STATS skipped = 0; #endif /* free possible loaded avps -bogdan */ reset_avps(); #ifdef WITH_XAVP xavp_reset_list(); #endif LM_DBG("cleaning up\n"); free_sip_msg(msg); pkg_free(msg); #ifdef STATS if (skipped) STATS_RX_DROPS; #endif /* reset log prefix */ log_prefix_set(NULL); return 0; #ifndef NO_ONREPLY_ROUTE_ERROR error_rpl: /* execute post reply-script callbacks */ exec_post_script_cb(msg, ONREPLY_CB_TYPE); reset_avps(); #ifdef WITH_XAVP xavp_reset_list(); #endif goto error02; #endif /* NO_ONREPLY_ROUTE_ERROR */ error_req: LM_DBG("error:...\n"); /* execute post request-script callbacks */ exec_post_script_cb(msg, REQUEST_CB_TYPE); error03: /* free possible loaded avps -bogdan */ reset_avps(); #ifdef WITH_XAVP xavp_reset_list(); #endif error02: free_sip_msg(msg); pkg_free(msg); error00: STATS_RX_DROPS; /* reset log prefix */ log_prefix_set(NULL); return -1; }
/* reads all headers (until double crlf), & parses the content-length header * (WARNING: inefficient, tries to reuse receive_msg but will go through * the headers twice [once here looking for Content-Length and for the end * of the headers and once in receive_msg]; a more speed efficient version will * result in either major code duplication or major changes to the receive code) * returns number of bytes read & sets r->state & r->body * when either r->body!=0 or r->state==H_BODY => * all headers have been read. It should be called in a while loop. * returns < 0 if error or 0 if EOF */ int tcp_read_headers(struct tcp_connection *c, int* read_flags) { int bytes, remaining; char *p; struct tcp_req* r; unsigned int mc; /* magic cookie */ unsigned short body_len; #ifdef READ_MSRP char *mfline; str mtransid; #endif #define crlf_default_skip_case \ case '\n': \ r->state=H_LF; \ break; \ default: \ r->state=H_SKIP #define content_len_beg_case \ case ' ': \ case '\t': \ if (!TCP_REQ_HAS_CLEN(r)) r->state=H_STARTWS; \ else r->state=H_SKIP; \ /* not interested if we already found one */ \ break; \ case 'C': \ case 'c': \ if(!TCP_REQ_HAS_CLEN(r)) r->state=H_CONT_LEN1; \ else r->state=H_SKIP; \ break; \ case 'l': \ case 'L': \ /* short form for Content-Length */ \ if (!TCP_REQ_HAS_CLEN(r)) r->state=H_L_COLON; \ else r->state=H_SKIP; \ break #define change_state(upper, lower, newstate)\ switch(*p){ \ case upper: \ case lower: \ r->state=(newstate); break; \ crlf_default_skip_case; \ } #define change_state_case(state0, upper, lower, newstate)\ case state0: \ change_state(upper, lower, newstate); \ p++; \ break r=&c->req; /* if we still have some unparsed part, parse it first, don't do the read*/ if (unlikely(r->parsed<r->pos)){ bytes=0; }else{ #ifdef USE_TLS if (unlikely(c->type==PROTO_TLS)) bytes=tls_read(c, read_flags); else #endif bytes=tcp_read(c, read_flags); if (bytes<=0) return bytes; } p=r->parsed; while(p<r->pos && r->error==TCP_REQ_OK){ switch((unsigned char)r->state){ case H_BODY: /* read the body*/ remaining=r->pos-p; if (remaining>r->bytes_to_go) remaining=r->bytes_to_go; r->bytes_to_go-=remaining; p+=remaining; if (r->bytes_to_go==0){ r->flags|=F_TCP_REQ_COMPLETE; goto skip; } break; case H_SKIP: /* find lf, we are in this state if we are not interested * in anything till end of line*/ p=q_memchr(p, '\n', r->pos-p); if (p){ #ifdef READ_MSRP /* catch if it is MSRP or not with first '\n' */ if(!((r->flags&F_TCP_REQ_MSRP_NO) || (r->flags&F_TCP_REQ_MSRP_FRAME))) { if((r->pos - r->start)>5 && strncmp(r->start, "MSRP ", 5)==0) { r->flags |= F_TCP_REQ_MSRP_FRAME; } else { r->flags |= F_TCP_REQ_MSRP_NO; } } #endif p++; r->state=H_LF; }else{ p=r->pos; } break; case H_LF: /* terminate on LF CR LF or LF LF */ switch (*p){ case '\r': r->state=H_LFCR; break; case '\n': /* found LF LF */ r->state=H_BODY; #ifdef READ_HTTP11 if (cfg_get(tcp, tcp_cfg, accept_no_cl)!=0) tcp_http11_continue(c); #endif if (TCP_REQ_HAS_CLEN(r)){ r->body=p+1; r->bytes_to_go=r->content_len; if (r->bytes_to_go==0){ r->flags|=F_TCP_REQ_COMPLETE; p++; goto skip; } }else{ if(cfg_get(tcp, tcp_cfg, accept_no_cl)!=0) { #ifdef READ_MSRP /* if MSRP message */ if(c->req.flags&F_TCP_REQ_MSRP_FRAME) { r->body=p+1; /* at least 3 bytes: 0\r\n */ r->bytes_to_go=3; p++; r->content_len = 0; r->state=H_MSRP_BODY; break; } #endif #ifdef READ_HTTP11 if(TCP_REQ_BCHUNKED(r)) { r->body=p+1; /* at least 3 bytes: 0\r\n */ r->bytes_to_go=3; p++; r->content_len = 0; r->state=H_HTTP11_CHUNK_START; break; } #endif r->body=p+1; r->bytes_to_go=0; r->flags|=F_TCP_REQ_COMPLETE; p++; goto skip; } else { LM_DBG("ERROR: no clen, p=%X\n", *p); r->error=TCP_REQ_BAD_LEN; } } break; case '-': r->state=H_SKIP; #ifdef READ_MSRP /* catch end of MSRP frame without body * '-------sessid$\r\n' * follows headers wihtout extra CRLF */ if(r->flags&F_TCP_REQ_MSRP_FRAME) { p--; r->state=H_MSRP_BODY_END; } #endif break; content_len_beg_case; default: r->state=H_SKIP; } p++; break; case H_LFCR: if (*p=='\n'){ /* found LF CR LF */ r->state=H_BODY; #ifdef READ_HTTP11 if (cfg_get(tcp, tcp_cfg, accept_no_cl)!=0) tcp_http11_continue(c); #endif if (TCP_REQ_HAS_CLEN(r)){ r->body=p+1; r->bytes_to_go=r->content_len; if (r->bytes_to_go==0){ r->flags|=F_TCP_REQ_COMPLETE; p++; goto skip; } }else{ if (cfg_get(tcp, tcp_cfg, accept_no_cl)!=0) { #ifdef READ_MSRP /* if MSRP message */ if(c->req.flags&F_TCP_REQ_MSRP_FRAME) { r->body=p+1; /* at least 3 bytes: 0\r\n */ r->bytes_to_go=3; p++; r->content_len = 0; r->state=H_MSRP_BODY; break; } #endif #ifdef READ_HTTP11 if(TCP_REQ_BCHUNKED(r)) { r->body=p+1; /* at least 3 bytes: 0\r\n */ r->bytes_to_go=3; p++; r->content_len = 0; r->state=H_HTTP11_CHUNK_START; break; } #endif r->body=p+1; r->bytes_to_go=0; r->flags|=F_TCP_REQ_COMPLETE; p++; goto skip; } else { LM_DBG("ERROR: no clen, p=%X\n", *p); r->error=TCP_REQ_BAD_LEN; } } }else r->state=H_SKIP; p++; break; case H_STARTWS: switch (*p){ content_len_beg_case; crlf_default_skip_case; } p++; break; case H_SKIP_EMPTY: switch (*p){ case '\n': break; case '\r': if (cfg_get(tcp, tcp_cfg, crlf_ping)) { r->state=H_SKIP_EMPTY_CR_FOUND; r->start=p; } break; case ' ': case '\t': /* skip empty lines */ break; case 'C': case 'c': r->state=H_CONT_LEN1; r->start=p; break; case 'l': case 'L': /* short form for Content-Length */ r->state=H_L_COLON; r->start=p; break; default: /* stun test */ if (unlikely(sr_event_enabled(SREV_STUN_IN)) && (unsigned char)*p == 0x00) { r->state=H_STUN_MSG; /* body will used as pointer to the last used byte */ r->body=p; r->content_len = 0; LM_DBG("stun msg detected\n"); } else { r->state=H_SKIP; } r->start=p; }; p++; break; case H_SKIP_EMPTY_CR_FOUND: if (*p=='\n'){ r->state=H_SKIP_EMPTY_CRLF_FOUND; p++; }else{ r->state=H_SKIP_EMPTY; } break; case H_SKIP_EMPTY_CRLF_FOUND: if (*p=='\r'){ r->state = H_SKIP_EMPTY_CRLFCR_FOUND; p++; }else{ r->state = H_SKIP_EMPTY; } break; case H_SKIP_EMPTY_CRLFCR_FOUND: if (*p=='\n'){ r->state = H_PING_CRLF; r->flags |= F_TCP_REQ_HAS_CLEN | F_TCP_REQ_COMPLETE; /* hack to avoid error check */ p++; goto skip; }else{ r->state = H_SKIP_EMPTY; } break; case H_STUN_MSG: if ((r->pos - r->body) >= sizeof(struct stun_hdr)) { /* copy second short from buffer where should be body * length */ memcpy(&body_len, &r->start[sizeof(unsigned short)], sizeof(unsigned short)); body_len = ntohs(body_len); /* check if there is valid magic cookie */ memcpy(&mc, &r->start[sizeof(unsigned int)], sizeof(unsigned int)); mc = ntohl(mc); /* using has_content_len as a flag if there should be * fingerprint or no */ r->flags |= (mc == MAGIC_COOKIE) ? F_TCP_REQ_HAS_CLEN : 0; r->body += sizeof(struct stun_hdr); p = r->body; if (body_len > 0) { r->state = H_STUN_READ_BODY; } else { if (is_msg_complete(r) != 0) { goto skip; } else { /* set content_len to length of fingerprint */ body_len = sizeof(struct stun_attr) + 20; /* 20 is SHA_DIGEST_LENGTH from openssl/sha.h */ } } r->content_len=body_len; } else { p = r->pos; } break; case H_STUN_READ_BODY: /* check if the whole body was read */ body_len=r->content_len; if ((r->pos - r->body) >= body_len) { r->body += body_len; p = r->body; if (is_msg_complete(r) != 0) { r->content_len=0; goto skip; } else { /* set content_len to length of fingerprint */ body_len = sizeof(struct stun_attr) + 20; /* 20 is SHA_DIGEST_LENGTH from openssl/sha.h */ r->content_len=body_len; } } else { p = r->pos; } break; case H_STUN_FP: /* content_len contains length of fingerprint in this place! */ body_len=r->content_len; if ((r->pos - r->body) >= body_len) { r->body += body_len; p = r->body; r->state = H_STUN_END; r->flags |= F_TCP_REQ_COMPLETE | F_TCP_REQ_HAS_CLEN; /* hack to avoid error check */ r->content_len=0; goto skip; } else { p = r->pos; } break; change_state_case(H_CONT_LEN1, 'O', 'o', H_CONT_LEN2); change_state_case(H_CONT_LEN2, 'N', 'n', H_CONT_LEN3); change_state_case(H_CONT_LEN3, 'T', 't', H_CONT_LEN4); change_state_case(H_CONT_LEN4, 'E', 'e', H_CONT_LEN5); change_state_case(H_CONT_LEN5, 'N', 'n', H_CONT_LEN6); change_state_case(H_CONT_LEN6, 'T', 't', H_CONT_LEN7); change_state_case(H_CONT_LEN7, '-', '_', H_CONT_LEN8); change_state_case(H_CONT_LEN8, 'L', 'l', H_CONT_LEN9); change_state_case(H_CONT_LEN9, 'E', 'e', H_CONT_LEN10); change_state_case(H_CONT_LEN10, 'N', 'n', H_CONT_LEN11); change_state_case(H_CONT_LEN11, 'G', 'g', H_CONT_LEN12); change_state_case(H_CONT_LEN12, 'T', 't', H_CONT_LEN13); change_state_case(H_CONT_LEN13, 'H', 'h', H_L_COLON); case H_L_COLON: switch(*p){ case ' ': case '\t': break; /* skip space */ case ':': r->state=H_CONT_LEN_BODY; break; crlf_default_skip_case; }; p++; break; case H_CONT_LEN_BODY: switch(*p){ case ' ': case '\t': break; /* eat space */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': r->state=H_CONT_LEN_BODY_PARSE; r->content_len=(*p-'0'); break; /*FIXME: content length on different lines ! */ crlf_default_skip_case; } p++; break; case H_CONT_LEN_BODY_PARSE: switch(*p){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': r->content_len=r->content_len*10+(*p-'0'); break; case '\r': case ' ': case '\t': /* FIXME: check if line contains only WS */ if(r->content_len<0) { LM_ERR("bad Content-Length header value %d in" " state %d\n", r->content_len, r->state); r->content_len=0; r->error=TCP_REQ_BAD_LEN; r->state=H_SKIP; /* skip now */ } r->state=H_SKIP; r->flags|=F_TCP_REQ_HAS_CLEN; break; case '\n': /* end of line, parse successful */ if(r->content_len<0) { LM_ERR("bad Content-Length header value %d in" " state %d\n", r->content_len, r->state); r->content_len=0; r->error=TCP_REQ_BAD_LEN; r->state=H_SKIP; /* skip now */ } r->state=H_LF; r->flags|=F_TCP_REQ_HAS_CLEN; break; default: LM_ERR("bad Content-Length header value, unexpected " "char %c in state %d\n", *p, r->state); r->state=H_SKIP; /* try to find another?*/ } p++; break; #ifdef READ_HTTP11 case H_HTTP11_CHUNK_START: /* start a new body chunk: SIZE\r\nBODY\r\n */ r->chunk_size = 0; r->state = H_HTTP11_CHUNK_SIZE; break; case H_HTTP11_CHUNK_BODY: /* content of chunnk */ remaining=r->pos-p; if (remaining>r->bytes_to_go) remaining=r->bytes_to_go; r->bytes_to_go-=remaining; p+=remaining; if (r->bytes_to_go==0){ r->state = H_HTTP11_CHUNK_END; /* shift back body content */ if(r->chunk_size>0 && p-r->chunk_size>r->body) { memmove(r->body + r->content_len, p - r->chunk_size, r->chunk_size); r->content_len += r->chunk_size; } goto skip; } break; case H_HTTP11_CHUNK_END: switch(*p){ case '\r': case ' ': case '\t': /* skip */ break; case '\n': r->state = H_HTTP11_CHUNK_START; break; default: LM_ERR("bad chunk, unexpected " "char %c in state %d\n", *p, r->state); r->state=H_SKIP; /* try to find another?*/ } p++; break; case H_HTTP11_CHUNK_SIZE: switch(*p){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': r->chunk_size <<= 4; r->chunk_size += *p - '0'; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': r->chunk_size <<= 4; r->chunk_size += *p - 'a' + 10; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': r->chunk_size <<= 4; r->chunk_size += *p - 'A' + 10; break; case '\r': case ' ': case '\t': /* skip */ break; case '\n': /* end of line, parse successful */ r->state=H_HTTP11_CHUNK_BODY; r->bytes_to_go = r->chunk_size; if (r->bytes_to_go==0){ r->state=H_HTTP11_CHUNK_FINISH; r->flags|=F_TCP_REQ_COMPLETE; p++; goto skip; } break; default: LM_ERR("bad chunk size value, unexpected " "char %c in state %d\n", *p, r->state); r->state=H_SKIP; /* try to find another?*/ } p++; break; #endif #ifdef READ_MSRP case H_MSRP_BODY: /* body of msrp frame */ /* find lf, we are in this state if we are not interested * in anything till end of line*/ r->flags |= F_TCP_REQ_MSRP_BODY; p = q_memchr(p, '\n', r->pos-p); if (p) { p++; r->state=H_MSRP_BODY_LF; } else { p=r->pos; } break; case H_MSRP_BODY_LF: /* LF in body of msrp frame */ switch (*p) { case '-': p--; r->state=H_MSRP_BODY_END; break; default: r->state=H_MSRP_BODY; } p++; break; case H_MSRP_BODY_END: /* end of body for msrp frame */ /* find LF and check if it is end-line */ p = q_memchr(p, '\n', r->pos-p); if (p) { /* check if it is end line '-------sessid$\r\n' */ if(r->pos - r->start < 10) { LM_ERR("weird situation when reading MSRP frame" " - continue reading\n"); /* *p=='\n' */ r->state=H_MSRP_BODY_LF; p++; break; } if(*(p-1)!='\r') { /* not ending in '\r\n' - not end-line */ /* *p=='\n' */ r->state=H_MSRP_BODY_LF; p++; break; } /* locate transaction id in first line * -- first line exists, that's why we are here */ mfline = q_memchr(r->start, '\n', r->pos-r->start); mtransid.s = q_memchr(r->start + 5 /* 'MSRP ' */, ' ', mfline - r->start); mtransid.len = mtransid.s - r->start - 5; mtransid.s = r->start + 5; trim(&mtransid); if(memcmp(mtransid.s, p - 1 /*\r*/ - 1 /* '+'|'#'|'$' */ - mtransid.len, mtransid.len)!=0) { /* no match on session id - not end-line */ /* *p=='\n' */ r->state=H_MSRP_BODY_LF; p++; break; } if(memcmp(p - 1 /*\r*/ - 1 /* '+'|'#'|'$' */ - mtransid.len - 7 /* 7 x '-' */ - 1 /* '\n' */, "\n-------", 8)!=0) { /* no match on "\n-------" - not end-line */ /* *p=='\n' */ r->state=H_MSRP_BODY_LF; p++; break; } r->state=H_MSRP_FINISH; r->flags|=F_TCP_REQ_COMPLETE; p++; goto skip; } else { p=r->pos; } break; #endif default: LM_CRIT("unexpected state %d\n", r->state); abort(); } } skip: r->parsed=p; return bytes; }
int msrp_relay(msrp_frame_t *mf) { struct dest_info *dst; struct tcp_connection *con = NULL; char reqbuf[MSRP_MAX_FRAME_SIZE]; msrp_hdr_t *tpath; msrp_hdr_t *fpath; msrp_env_t *env; str_array_t *sar; char *p; char *l; int port; if(mf->buf.len>=MSRP_MAX_FRAME_SIZE-1) return -1; tpath = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH); if(tpath==NULL) { LM_ERR("To-Path header not found\n"); return -1; } fpath = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH); if(fpath==NULL) { LM_ERR("From-Path header not found\n"); return -1; } l = q_memchr(tpath->body.s, ' ', tpath->body.len); if(l==NULL) { LM_DBG("To-Path has only one URI -- nowehere to forward\n"); return -1; } p = reqbuf; memcpy(p, mf->buf.s, tpath->body.s - mf->buf.s); p += tpath->body.s - mf->buf.s; memcpy(p, l + 1, fpath->body.s - l - 1); p += fpath->body.s - l - 1; memcpy(p, tpath->body.s, l + 1 - tpath->body.s); p += l + 1 - tpath->body.s; memcpy(p, fpath->name.s + 11, mf->buf.s + mf->buf.len - fpath->name.s - 11); p += mf->buf.s + mf->buf.len - fpath->name.s - 11; env = msrp_get_env(); if(env->envflags&MSRP_ENV_DSTINFO) { dst = &env->dstinfo; goto done; } if(msrp_parse_hdr_to_path(mf)<0) { LM_ERR("error parsing To-Path header\n"); return -1; } sar = (str_array_t*)tpath->parsed.data; if(sar==NULL || sar->size<2) { LM_DBG("To-Path has no next hop URI -- nowehere to forward\n"); return -1; } if(msrp_env_set_dstinfo(mf, &sar->list[1], NULL, 0)<0) { LM_ERR("unable to set destination address\n"); return -1; } dst = &env->dstinfo; done: if (dst->send_flags.f & SND_F_FORCE_CON_REUSE) { port = su_getport(&dst->to); if (likely(port)) { ticks_t con_lifetime; struct ip_addr ip; con_lifetime = cfg_get(tcp, tcp_cfg, con_lifetime); su2ip_addr(&ip, &dst->to); con = tcpconn_get(dst->id, &ip, port, NULL, con_lifetime); } else if (likely(dst->id)) { con = tcpconn_get(dst->id, 0, 0, 0, 0); } if (con == NULL) { LM_WARN("TCP/TLS connection not found\n"); return -1; } if (unlikely((con->rcv.proto == PROTO_WS || con->rcv.proto == PROTO_WSS) && sr_event_enabled(SREV_TCP_WS_FRAME_OUT))) { ws_event_info_t wsev; memset(&wsev, 0, sizeof(ws_event_info_t)); wsev.type = SREV_TCP_WS_FRAME_OUT; wsev.buf = reqbuf; wsev.len = p - reqbuf; wsev.id = con->id; return sr_event_exec(SREV_TCP_WS_FRAME_OUT, (void *) &wsev); } else if (tcp_send(dst, 0, reqbuf, p - reqbuf) < 0) { LM_ERR("forwarding frame failed\n"); return -1; } } else if (tcp_send(dst, 0, reqbuf, p - reqbuf) < 0) { LM_ERR("forwarding frame failed\n"); return -1; } return 0; }
int msrp_reply(msrp_frame_t *mf, str *code, str *text, str *xhdrs) { char rplbuf[MSRP_MAX_FRAME_SIZE]; msrp_hdr_t *hdr; msrp_env_t *env; char *p; char *l; /* no reply for a reply */ if(mf->fline.msgtypeid==MSRP_REPLY) return 0; if(mf->fline.msgtypeid==MSRP_REQ_REPORT) { /* it does not take replies */ return 0; } p = rplbuf; memcpy(p, mf->fline.protocol.s, mf->fline.protocol.len); p += mf->fline.protocol.len; *p = ' '; p++; memcpy(p, mf->fline.transaction.s, mf->fline.transaction.len); p += mf->fline.transaction.len; *p = ' '; p++; memcpy(p, code->s, code->len); p += code->len; *p = ' '; p++; memcpy(p, text->s, text->len); p += text->len; memcpy(p, "\r\n", 2); p += 2; memcpy(p, "To-Path: ", 9); p += 9; hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH); if(hdr==NULL) { LM_ERR("From-Path header not found\n"); return -1; } if(mf->fline.msgtypeid==MSRP_REQ_SEND) { l = q_memchr(hdr->body.s, ' ', hdr->body.len); if(l==NULL) { memcpy(p, hdr->body.s, hdr->body.len + 2); p += hdr->body.len + 2; } else { memcpy(p, hdr->body.s, l - hdr->body.s); p += l - hdr->body.s; memcpy(p, "\r\n", 2); p += 2; } } else { memcpy(p, hdr->body.s, hdr->body.len + 2); p += hdr->body.len + 2; } hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH); if(hdr==NULL) { LM_ERR("To-Path header not found\n"); return -1; } memcpy(p, "From-Path: ", 11); p += 11; l = q_memchr(hdr->body.s, ' ', hdr->body.len); if(l==NULL) { memcpy(p, hdr->body.s, hdr->body.len + 2); p += hdr->body.len + 2; } else { memcpy(p, hdr->body.s, l - hdr->body.s); p += l - hdr->body.s; memcpy(p, "\r\n", 2); p += 2; } hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_MESSAGE_ID); if(hdr!=NULL) { memcpy(p, hdr->buf.s, hdr->buf.len); p += hdr->buf.len; } if(xhdrs!=NULL && xhdrs->s!=NULL) { memcpy(p, xhdrs->s, xhdrs->len); p += xhdrs->len; } memcpy(p, mf->endline.s, mf->endline.len); p += mf->endline.len; *(p-3) = '$'; env = msrp_get_env(); if (unlikely((env->srcinfo.proto == PROTO_WS || env->srcinfo.proto == PROTO_WSS) && sr_event_enabled(SREV_TCP_WS_FRAME_OUT))) { struct tcp_connection *con = tcpconn_get(env->srcinfo.id, 0, 0, 0, 0); ws_event_info_t wsev; if (con == NULL) { LM_WARN("TCP/TLS connection for WebSocket could not be" "found\n"); return -1; } memset(&wsev, 0, sizeof(ws_event_info_t)); wsev.type = SREV_TCP_WS_FRAME_OUT; wsev.buf = rplbuf; wsev.len = p - rplbuf; wsev.id = con->id; return sr_event_exec(SREV_TCP_WS_FRAME_OUT, (void *) &wsev); } else if (tcp_send(&env->srcinfo, 0, rplbuf, p - rplbuf) < 0) { LM_ERR("sending reply failed\n"); return -1; } return 0; }