/** * Request the fd corresponding to the given connection id to the TCP main process. * You may want to close() the fd after use. * * @param conid - connection id * @param fd - placeholder to return the fd * @return 1 on success, 0 on failure * */ int tcpops_acquire_fd_from_tcpmain(int conid, int *fd) { struct tcp_connection *s_con, *tmp; long msg[2]; int n; if (unlikely((s_con = tcpconn_get(conid, 0, 0, 0, 0)) == NULL)) { LM_ERR("invalid connection id %d, (must be a TCP connid)\n", conid); return 0; } msg[0] = (long)s_con; msg[1] = CONN_GET_FD; n = send_all(unix_tcp_sock, msg, sizeof(msg)); if (unlikely(n <= 0)){ LM_ERR("failed to send fd request: %s (%d)\n", strerror(errno), errno); goto error_release; } n = receive_fd(unix_tcp_sock, &tmp, sizeof(tmp), fd, MSG_WAITALL); if (unlikely(n <= 0)){ LM_ERR("failed to get fd (receive_fd): %s (%d)\n", strerror(errno), errno); goto error_release; } tcpconn_put(s_con); return 1; error_release: tcpconn_put(s_con); return 0; }
static int ws_rpc_add_node(rpc_t* rpc, void* ctx, void* ih, ws_connection_t *wsc) { int interval; char *src_proto, *dst_proto, *pong, *sub_protocol; char src_ip[IP6_MAX_STR_SIZE + 1], dst_ip[IP6_MAX_STR_SIZE + 1]; struct tcp_connection *con = tcpconn_get(wsc->id, 0, 0, 0, 0); char rplbuf[512]; if (con) { src_proto = (con->rcv.proto== PROTO_WS) ? "ws" : "wss"; memset(src_ip, 0, IP6_MAX_STR_SIZE + 1); ip_addr2sbuf(&con->rcv.src_ip, src_ip, IP6_MAX_STR_SIZE); dst_proto = (con->rcv.proto == PROTO_WS) ? "ws" : "wss"; memset(dst_ip, 0, IP6_MAX_STR_SIZE + 1); ip_addr2sbuf(&con->rcv.dst_ip, dst_ip, IP6_MAX_STR_SIZE); pong = wsc->awaiting_pong ? "awaiting Pong, " : ""; interval = (int)time(NULL) - wsc->last_used; if (wsc->sub_protocol == SUB_PROTOCOL_SIP) sub_protocol = "sip"; else if (wsc->sub_protocol == SUB_PROTOCOL_MSRP) sub_protocol = "msrp"; else sub_protocol = "**UNKNOWN**"; if (snprintf(rplbuf, 512, "%d: %s:%s:%hu -> %s:%s:%hu (state: %s" ", %s last used %ds ago" ", sub-protocol: %s)", wsc->id, src_proto, strlen(src_ip) ? src_ip : "*", con->rcv.src_port, dst_proto, strlen(dst_ip) ? dst_ip : "*", con->rcv.dst_port, wsconn_state_str[wsc->state], pong, interval, sub_protocol) < 0) { tcpconn_put(con); rpc->fault(ctx, 500, "Failed to print connection details"); return -1; } if (rpc->array_add(ih, "s", rplbuf)<0) { tcpconn_put(con); rpc->fault(ctx, 500, "Failed to add to response"); return -1; } tcpconn_put(con); return 1; } else return 0; }
static int add_node(struct mi_root *tree, ws_connection_t *wsc) { int interval; char *src_proto, *dst_proto, *pong, *sub_protocol; char src_ip[IP6_MAX_STR_SIZE + 1], dst_ip[IP6_MAX_STR_SIZE + 1]; struct tcp_connection *con = tcpconn_get(wsc->id, 0, 0, 0, 0); if (con) { src_proto = (con->rcv.proto== PROTO_WS) ? "ws" : "wss"; memset(src_ip, 0, IP6_MAX_STR_SIZE + 1); ip_addr2sbuf(&con->rcv.src_ip, src_ip, IP6_MAX_STR_SIZE); dst_proto = (con->rcv.proto == PROTO_WS) ? "ws" : "wss"; memset(dst_ip, 0, IP6_MAX_STR_SIZE + 1); ip_addr2sbuf(&con->rcv.dst_ip, dst_ip, IP6_MAX_STR_SIZE); pong = wsc->awaiting_pong ? "awaiting Pong, " : ""; interval = (int)time(NULL) - wsc->last_used; if (wsc->sub_protocol == SUB_PROTOCOL_SIP) sub_protocol = "sip"; else if (wsc->sub_protocol == SUB_PROTOCOL_MSRP) sub_protocol = "msrp"; else sub_protocol = "**UNKNOWN**"; if (addf_mi_node_child(&tree->node, 0, 0, 0, "%d: %s:%s:%hu -> %s:%s:%hu (state: %s" ", %s last used %ds ago" ", sub-protocol: %s)", wsc->id, src_proto, strlen(src_ip) ? src_ip : "*", con->rcv.src_port, dst_proto, strlen(dst_ip) ? dst_ip : "*", con->rcv.dst_port, wsconn_state_str[wsc->state], pong, interval, sub_protocol) == 0) { tcpconn_put(con); return -1; } tcpconn_put(con); return 1; } else return 0; }
/** * gets the fd of the current message source connection * * @param conid - connection id * @param fd - placeholder to return the fd * @return 1 on success, 0 on failure * */ int tcpops_get_current_fd(int conid, int *fd) { struct tcp_connection *s_con; if (unlikely((s_con = tcpconn_get(conid, 0, 0, 0, 0)) == NULL)) { LM_ERR("invalid connection id %d, (must be a TCP connid)\n", conid); return 0; } LM_DBG("got fd=%d from id=%d\n", s_con->fd, conid); *fd = s_con->fd; tcpconn_put(s_con); return 1; }
struct tcp_connection* get_cur_connection(struct sip_msg* msg) { struct tcp_connection* c; if (msg->rcv.proto != PROTO_TLS) { LM_ERR("transport protocol is not TLS (bug in config)\n"); return 0; } c = tcpconn_get(msg->rcv.proto_reserved1, 0, 0, tcp_con_lifetime); if (c && c->type != PROTO_TLS) { LM_ERR("connection found but is not TLS (bug in config)\n"); tcpconn_put(c); return 0; } return c; }
void wsconn_close_now(ws_connection_t *wsc) { struct tcp_connection *con = tcpconn_get(wsc->id, 0, 0, 0, 0); if (wsconn_rm(wsc, WSCONN_EVENTROUTE_YES) < 0) LM_ERR("removing WebSocket connection\n"); if (con == NULL) { LM_ERR("getting TCP/TLS connection\n"); return; } tcpconn_put(con); con->send_flags.f |= SND_F_CON_CLOSE; con->state = S_CONN_BAD; con->timeout = get_ticks_raw(); }
struct tcp_connection* get_cur_connection(struct sip_msg* msg) { struct tcp_connection* c; if(_tls_pv_con != 0) return _tls_pv_con; if (msg->rcv.proto != PROTO_TLS) { ERR("Transport protocol is not TLS (bug in config)\n"); return 0; } c = tcpconn_get(msg->rcv.proto_reserved1, 0, 0, 0, cfg_get(tls, tls_cfg, con_lifetime)); if (c && c->type != PROTO_TLS) { ERR("Connection found but is not TLS\n"); tcpconn_put(c); return 0; } return c; }
static int is_peer_verified(struct sip_msg* msg, char* foo, char* foo2) { struct tcp_connection *c; SSL *ssl; long ssl_verify; X509 *x509_cert; DBG("started...\n"); if (msg->rcv.proto != PROTO_TLS) { ERR("proto != TLS --> peer can't be verified, return -1\n"); return -1; } DBG("trying to find TCP connection of received message...\n"); c = tcpconn_get(msg->rcv.proto_reserved1, 0, 0, 0, cfg_get(tls, tls_cfg, con_lifetime)); if (!c) { ERR("connection no longer exits\n"); return -1; } if(c->type != PROTO_TLS) { ERR("Connection found but is not TLS\n"); tcpconn_put(c); return -1; } if (!c->extra_data) { LM_ERR("no extra_data specified in TLS/TCP connection found." " This should not happen... return -1\n"); tcpconn_put(c); return -1; } ssl = ((struct tls_extra_data*)c->extra_data)->ssl; ssl_verify = SSL_get_verify_result(ssl); if ( ssl_verify != X509_V_OK ) { LM_WARN("verification of presented certificate failed... return -1\n"); tcpconn_put(c); return -1; } /* now, we have only valid peer certificates or peers without certificates. * Thus we have to check for the existence of a peer certificate */ x509_cert = SSL_get_peer_certificate(ssl); if ( x509_cert == NULL ) { LM_INFO("tlsops:is_peer_verified: WARNING: peer did not present " "a certificate. Thus it could not be verified... return -1\n"); tcpconn_put(c); return -1; } X509_free(x509_cert); tcpconn_put(c); LM_DBG("tlsops:is_peer_verified: peer is successfully verified" "...done\n"); return 1; }
/* finds a tcpconn & sends on it */ int tcp_send(int type, char* buf, unsigned len, union sockaddr_union* to, int id) { struct tcp_connection *c; struct tcp_connection *tmp; struct ip_addr ip; int port; int fd; long response[2]; int n; port=0; if (to){ su2ip_addr(&ip, to); port=su_getport(to); c=tcpconn_get(id, &ip, port, TCP_CON_SEND_TIMEOUT); }else if (id){ c=tcpconn_get(id, 0, 0, TCP_CON_SEND_TIMEOUT); }else{ LOG(L_CRIT, "BUG: tcp_send called with null id & to\n"); return -1; } if (id){ if (c==0) { if (to){ /* try again w/o id */ c=tcpconn_get(0, &ip, port, TCP_CON_SEND_TIMEOUT); goto no_id; }else{ LOG(L_ERR, "ERROR: tcp_send: id %d not found, dropping\n", id); return -1; } }else goto get_fd; } no_id: if (c==0){ DBG("tcp_send: no open tcp connection found, opening new one\n"); /* create tcp connection */ if ((c=tcpconn_connect(to, type))==0){ LOG(L_ERR, "ERROR: tcp_send: connect failed\n"); return -1; } c->refcnt++; /* safe to do it w/o locking, it's not yet available to the rest of the world */ fd=c->s; /* send the new tcpconn to "tcp main" */ response[0]=(long)c; response[1]=CONN_NEW; n=send_all(unix_tcp_sock, response, sizeof(response)); if (n<=0){ LOG(L_ERR, "BUG: tcp_send: failed write: %s (%d)\n", strerror(errno), errno); n=-1; goto end; } n=send_fd(unix_tcp_sock, &c, sizeof(c), c->s); if (n<=0){ LOG(L_ERR, "BUG: tcp_send: failed send_fd: %s (%d)\n", strerror(errno), errno); n=-1; goto end; } goto send_it; } get_fd: /* todo: see if this is not the same process holding * c and if so send directly on c->fd */ DBG("tcp_send: tcp connection found (%p), acquiring fd\n", c); /* get the fd */ response[0]=(long)c; response[1]=CONN_GET_FD; n=send_all(unix_tcp_sock, response, sizeof(response)); if (n<=0){ LOG(L_ERR, "BUG: tcp_send: failed to get fd(write):%s (%d)\n", strerror(errno), errno); n=-1; goto release_c; } DBG("tcp_send, c= %p, n=%d\n", c, n); tmp=c; n=receive_fd(unix_tcp_sock, &c, sizeof(c), &fd); if (n<=0){ LOG(L_ERR, "BUG: tcp_send: failed to get fd(receive_fd):" " %s (%d)\n", strerror(errno), errno); n=-1; goto release_c; } if (c!=tmp){ LOG(L_CRIT, "BUG: tcp_send: get_fd: got different connection:" " %p (id= %d, refcnt=%d state=%d != " " %p (id= %d, refcnt=%d state=%d (n=%d)\n", c, c->id, c->refcnt, c->state, tmp, tmp->id, tmp->refcnt, tmp->state, n ); n=-1; /* fail */ goto end; } DBG("tcp_send: after receive_fd: c= %p n=%d fd=%d\n",c, n, fd); send_it: DBG("tcp_send: sending...\n"); lock_get(&c->write_lock); #ifdef USE_TLS if (c->type==PROTO_TLS) n=tls_blocking_write(c, fd, buf, len); else #endif n=tcp_blocking_write(c, fd, buf, len); lock_release(&c->write_lock); DBG("tcp_send: after write: c= %p n=%d fd=%d\n",c, n, fd); DBG("tcp_send: buf=\n%.*s\n", (int)len, buf); if (n<0){ LOG(L_ERR, "ERROR: tcp_send: failed to send\n"); /* error on the connection , mark it as bad and set 0 timeout */ c->state=S_CONN_BAD; c->timeout=0; /* tell "main" it should drop this (optional it will t/o anyway?)*/ response[0]=(long)c; response[1]=CONN_ERROR; n=send_all(unix_tcp_sock, response, sizeof(response)); /* CONN_ERROR will auto-dec refcnt => we must not call tcpconn_put !!*/ if (n<=0){ LOG(L_ERR, "BUG: tcp_send: error return failed (write):%s (%d)\n", strerror(errno), errno); n=-1; } close(fd); return n; /* error return, no tcpconn_put */ } end: close(fd); release_c: tcpconn_put(c); /* release c (lock; dec refcnt; unlock) */ return n; }
int ws_handle_handshake(struct sip_msg *msg) { str key = {0, 0}, headers = {0, 0}, reply_key = {0, 0}, origin = {0, 0}; unsigned char sha1[SHA_DIGEST_LENGTH]; unsigned int hdr_flags = 0, sub_protocol = 0; int version = 0; struct hdr_field *hdr = msg->headers; struct tcp_connection *con; ws_connection_t *wsc; /* Make sure that the connection is closed after the response _and_ the existing connection (from the request) is reused for the response. The close flag will be unset later if the handshake is successful. */ msg->rpl_send_flags.f |= SND_F_CON_CLOSE; msg->rpl_send_flags.f |= SND_F_FORCE_CON_REUSE; if (cfg_get(websocket, ws_cfg, enabled) == 0) { LM_INFO("disabled: bouncing handshake\n"); ws_send_reply(msg, 503, &str_status_service_unavailable, NULL); return 0; } /* Retrieve TCP/TLS connection */ if ((con = tcpconn_get(msg->rcv.proto_reserved1, 0, 0, 0, 0)) == NULL) { LM_ERR("retrieving connection\n"); ws_send_reply(msg, 500, &str_status_internal_server_error, NULL); return 0; } if (con->type != PROTO_TCP && con->type != PROTO_TLS) { LM_ERR("unsupported transport: %d", con->type); goto end; } if (parse_headers(msg, HDR_EOH_F, 0) < 0) { LM_ERR("error parsing headers\n"); ws_send_reply(msg, 500, &str_status_internal_server_error, NULL); goto end; } /* Process HTTP headers */ while (hdr != NULL) { /* Decode and validate Connection */ if (cmp_hdrname_strzn(&hdr->name, str_hdr_connection.s, str_hdr_connection.len) == 0) { strlower(&hdr->body); if (str_search(&hdr->body, &str_upgrade) != NULL) { LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s, hdr->body.len, hdr->body.s); hdr_flags |= CONNECTION; } } /* Decode and validate Upgrade */ else if (cmp_hdrname_strzn(&hdr->name, str_hdr_upgrade.s, str_hdr_upgrade.len) == 0) { strlower(&hdr->body); if (str_search(&hdr->body, &str_websocket) != NULL) { LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s, hdr->body.len, hdr->body.s); hdr_flags |= UPGRADE; } } /* Decode and validate Sec-WebSocket-Key */ else if (cmp_hdrname_strzn(&hdr->name, str_hdr_sec_websocket_key.s, str_hdr_sec_websocket_key.len) == 0) { if (hdr_flags & SEC_WEBSOCKET_KEY) { LM_WARN("%.*s found multiple times\n", hdr->name.len, hdr->name.s); ws_send_reply(msg, 400, &str_status_bad_request, NULL); goto end; } LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s, hdr->body.len, hdr->body.s); key = hdr->body; hdr_flags |= SEC_WEBSOCKET_KEY; } /* Decode and validate Sec-WebSocket-Protocol */ else if (cmp_hdrname_strzn(&hdr->name, str_hdr_sec_websocket_protocol.s, str_hdr_sec_websocket_protocol.len) == 0) { strlower(&hdr->body); if (str_search(&hdr->body, &str_sip) != NULL) { LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s, hdr->body.len, hdr->body.s); hdr_flags |= SEC_WEBSOCKET_PROTOCOL; sub_protocol |= SUB_PROTOCOL_SIP; } if (str_search(&hdr->body, &str_msrp) != NULL) { LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s, hdr->body.len, hdr->body.s); hdr_flags |= SEC_WEBSOCKET_PROTOCOL; sub_protocol |= SUB_PROTOCOL_MSRP; } } /* Decode and validate Sec-WebSocket-Version */ else if (cmp_hdrname_strzn(&hdr->name, str_hdr_sec_websocket_version.s, str_hdr_sec_websocket_version.len) == 0) { if (hdr_flags & SEC_WEBSOCKET_VERSION) { LM_WARN("%.*s found multiple times\n", hdr->name.len, hdr->name.s); ws_send_reply(msg, 400, &str_status_bad_request, NULL); goto end; } str2sint(&hdr->body, &version); if (version != WS_VERSION) { LM_WARN("Unsupported protocol version %.*s\n", hdr->body.len, hdr->body.s); headers.s = headers_buf; headers.len = snprintf(headers.s, HDR_BUF_LEN, "%.*s: %d\r\n", str_hdr_sec_websocket_version.len, str_hdr_sec_websocket_version.s, WS_VERSION); ws_send_reply(msg, 426, &str_status_upgrade_required, &headers); goto end; } LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s, hdr->body.len, hdr->body.s); hdr_flags |= SEC_WEBSOCKET_VERSION; } /* Decode Origin */ else if (cmp_hdrname_strzn(&hdr->name, str_hdr_origin.s, str_hdr_origin.len) == 0) { if (hdr_flags & ORIGIN) { LM_WARN("%.*s found multiple times\n", hdr->name.len, hdr->name.s); ws_send_reply(msg, 400, &str_status_bad_request, NULL); goto end; } LM_DBG("found %.*s: %.*s\n", hdr->name.len, hdr->name.s, hdr->body.len, hdr->body.s); origin = hdr->body; hdr_flags |= ORIGIN; } hdr = hdr->next; } /* Final check that all required headers/values were found */ sub_protocol &= ws_sub_protocols; if ((hdr_flags & REQUIRED_HEADERS) != REQUIRED_HEADERS || sub_protocol == 0) { LM_WARN("required headers not present\n"); headers.s = headers_buf; headers.len = 0; if (ws_sub_protocols & SUB_PROTOCOL_SIP) headers.len += snprintf(headers.s + headers.len, HDR_BUF_LEN - headers.len, "%.*s: %.*s\r\n", str_hdr_sec_websocket_protocol.len, str_hdr_sec_websocket_protocol.s, str_sip.len, str_sip.s); if (ws_sub_protocols & SUB_PROTOCOL_MSRP) headers.len += snprintf(headers.s + headers.len, HDR_BUF_LEN - headers.len, "%.*s: %.*s\r\n", str_hdr_sec_websocket_protocol.len, str_hdr_sec_websocket_protocol.s, str_msrp.len, str_msrp.s); headers.len += snprintf(headers.s + headers.len, HDR_BUF_LEN - headers.len, "%.*s: %d\r\n", str_hdr_sec_websocket_version.len, str_hdr_sec_websocket_version.s, WS_VERSION); ws_send_reply(msg, 400, &str_status_bad_request, &headers); goto end; } /* Construct reply_key */ reply_key.s = (char *) pkg_malloc( (key.len + str_ws_guid.len) * sizeof(char)); if (reply_key.s == NULL) { LM_ERR("allocating pkg memory\n"); ws_send_reply(msg, 500, &str_status_internal_server_error, NULL); goto end; } memcpy(reply_key.s, key.s, key.len); memcpy(reply_key.s + key.len, str_ws_guid.s, str_ws_guid.len); reply_key.len = key.len + str_ws_guid.len; SHA1((const unsigned char *) reply_key.s, reply_key.len, sha1); pkg_free(reply_key.s); reply_key.s = key_buf; reply_key.len = base64_enc(sha1, SHA_DIGEST_LENGTH, (unsigned char *) reply_key.s, base64_enc_len(SHA_DIGEST_LENGTH)); /* Add the connection to the WebSocket connection table */ wsconn_add(msg->rcv, sub_protocol); /* Make sure Kamailio core sends future messages on this connection directly to this module */ if (con->type == PROTO_TLS) con->type = con->rcv.proto = PROTO_WSS; else con->type = con->rcv.proto = PROTO_WS; /* Now Kamailio is ready to receive WebSocket frames build and send a 101 reply */ headers.s = headers_buf; headers.len = 0; if (ws_cors_mode == CORS_MODE_ANY) headers.len += snprintf(headers.s + headers.len, HDR_BUF_LEN - headers.len, "%.*s: *\r\n", str_hdr_access_control_allow_origin.len, str_hdr_access_control_allow_origin.s); else if (ws_cors_mode == CORS_MODE_ORIGIN && origin.len > 0) headers.len += snprintf(headers.s + headers.len, HDR_BUF_LEN - headers.len, "%.*s: %.*s\r\n", str_hdr_access_control_allow_origin.len, str_hdr_access_control_allow_origin.s, origin.len, origin.s); if (sub_protocol & SUB_PROTOCOL_SIP) headers.len += snprintf(headers.s + headers.len, HDR_BUF_LEN - headers.len, "%.*s: %.*s\r\n", str_hdr_sec_websocket_protocol.len, str_hdr_sec_websocket_protocol.s, str_sip.len, str_sip.s); else if (sub_protocol & SUB_PROTOCOL_MSRP) headers.len += snprintf(headers.s + headers.len, HDR_BUF_LEN - headers.len, "%.*s: %.*s\r\n", str_hdr_sec_websocket_protocol.len, str_hdr_sec_websocket_protocol.s, str_msrp.len, str_msrp.s); headers.len += snprintf(headers.s + headers.len, HDR_BUF_LEN - headers.len, "%.*s: %.*s\r\n" "%.*s: %.*s\r\n" "%.*s: %.*s\r\n", str_hdr_upgrade.len, str_hdr_upgrade.s, str_websocket.len, str_websocket.s, str_hdr_connection.len, str_hdr_connection.s, str_upgrade.len, str_upgrade.s, str_hdr_sec_websocket_accept.len, str_hdr_sec_websocket_accept.s, reply_key.len, reply_key.s); msg->rpl_send_flags.f &= ~SND_F_CON_CLOSE; if (ws_send_reply(msg, 101, &str_status_switching_protocols, &headers) < 0) { if ((wsc = wsconn_get(msg->rcv.proto_reserved1)) != NULL) wsconn_rm(wsc, WSCONN_EVENTROUTE_NO); goto end; } else { if (sub_protocol & SUB_PROTOCOL_SIP) update_stat(ws_sip_successful_handshakes, 1); else if (sub_protocol & SUB_PROTOCOL_MSRP) update_stat(ws_msrp_successful_handshakes, 1); } tcpconn_put(con); return 1; end: if (con) tcpconn_put(con); return 0; }
/*! \brief Finds a tcpconn & sends on it */ int tcp_send(struct socket_info* send_sock, int type, char* buf, unsigned len, union sockaddr_union* to, int id) { struct tcp_connection *c; struct tcp_connection *tmp; struct ip_addr ip; int port; int fd; long response[2]; int n; struct timeval get,rcv,snd; port=0; reset_tcp_vars(tcpthreshold); start_expire_timer(get,tcpthreshold); if (to){ su2ip_addr(&ip, to); port=su_getport(to); c=tcpconn_get(id, &ip, port, tcp_con_lifetime); }else if (id){ c=tcpconn_get(id, 0, 0, tcp_con_lifetime); }else{ LM_CRIT("tcp_send called with null id & to\n"); get_time_difference(get,tcpthreshold,tcp_timeout_con_get); return -1; } if (id){ if (c==0) { if (to){ /* try again w/o id */ c=tcpconn_get(0, &ip, port, tcp_con_lifetime); goto no_id; }else{ LM_ERR("id %d not found, dropping\n", id); get_time_difference(get,tcpthreshold,tcp_timeout_con_get); return -1; } }else goto get_fd; } no_id: if (c==0){ if (tcp_no_new_conn) { return -1; } LM_DBG("no open tcp connection found, opening new one\n"); /* create tcp connection */ if ((c=tcpconn_connect(send_sock, to, type))==0){ LM_ERR("connect failed\n"); get_time_difference(get,tcpthreshold,tcp_timeout_con_get); return -1; } c->refcnt++; /* safe to do it w/o locking, it's not yet available to the rest of the world */ fd=c->s; /* send the new tcpconn to "tcp main" */ response[0]=(long)c; response[1]=CONN_NEW; n=send_fd(unix_tcp_sock, response, sizeof(response), c->s); get_time_difference(get,tcpthreshold,tcp_timeout_con_get); if (n<=0){ LM_ERR("failed send_fd: %s (%d)\n", strerror(errno), errno); n=-1; goto end; } goto send_it; } get_fd: get_time_difference(get,tcpthreshold,tcp_timeout_con_get); /* todo: see if this is not the same process holding * c and if so send directly on c->fd */ LM_DBG("tcp connection found (%p), acquiring fd\n", c); /* get the fd */ response[0]=(long)c; response[1]=CONN_GET_FD; start_expire_timer(rcv,tcpthreshold); n=send_all(unix_tcp_sock, response, sizeof(response)); if (n<=0){ LM_ERR("failed to get fd(write):%s (%d)\n", strerror(errno), errno); n=-1; get_time_difference(rcv,tcpthreshold,tcp_timeout_receive_fd); goto release_c; } LM_DBG("c= %p, n=%d\n", c, n); tmp=c; n=receive_fd(unix_tcp_sock, &c, sizeof(c), &fd, MSG_WAITALL); get_time_difference(rcv,tcpthreshold,tcp_timeout_receive_fd); if (n<=0){ LM_ERR("failed to get fd(receive_fd):" " %s (%d)\n", strerror(errno), errno); n=-1; goto release_c; } if (c!=tmp){ LM_CRIT("got different connection:" " %p (id= %d, refcnt=%d state=%d != " " %p (id= %d, refcnt=%d state=%d (n=%d)\n", c, c->id, c->refcnt, c->state, tmp, tmp->id, tmp->refcnt, tmp->state, n ); n=-1; /* fail */ goto end; } LM_DBG("after receive_fd: c= %p n=%d fd=%d\n",c, n, fd); send_it: LM_DBG("sending...\n"); lock_get(&c->write_lock); #ifdef USE_TLS if (c->type==PROTO_TLS) n=tls_blocking_write(c, fd, buf, len); else #endif /* n=tcp_blocking_write(c, fd, buf, len); */ start_expire_timer(snd,tcpthreshold); n=tsend_stream(fd, buf, len, tcp_send_timeout*1000); get_time_difference(snd,tcpthreshold,tcp_timeout_send); stop_expire_timer(get,tcpthreshold,0,buf,(int)len,1); lock_release(&c->write_lock); LM_DBG("after write: c= %p n=%d fd=%d\n",c, n, fd); LM_DBG("buf=\n%.*s\n", (int)len, buf); if (n<0){ LM_ERR("failed to send\n"); /* error on the connection , mark it as bad and set 0 timeout */ c->state=S_CONN_BAD; c->timeout=0; /* tell "main" it should drop this (optional it will t/o anyway?)*/ response[0]=(long)c; response[1]=CONN_ERROR; n=send_all(unix_tcp_sock, response, sizeof(response)); /* CONN_ERROR will auto-dec refcnt => we must not call tcpconn_put !!*/ if (n<=0){ LM_ERR("return failed (write):%s (%d)\n", strerror(errno), errno); } close(fd); return -1; /* error return, no tcpconn_put */ } end: close(fd); release_c: tcpconn_put(c); /* release c (lock; dec refcnt; unlock) */ return n; }
static int is_peer_verified(struct sip_msg* msg, char* foo, char* foo2) { struct tcp_connection *c; SSL *ssl; long ssl_verify; X509 *x509_cert; LM_DBG("started...\n"); if (msg->rcv.proto != PROTO_TLS) { LM_ERR("proto != TLS --> peer can't be verified, return -1\n"); return -1; } LM_DBG("trying to find TCP connection of received message...\n"); /* what if we have multiple connections to the same remote socket? e.g. we can have connection 1: localIP1:localPort1 <--> remoteIP:remotePort connection 2: localIP2:localPort2 <--> remoteIP:remotePort but I think the is very unrealistic */ c=tcpconn_get(0, &(msg->rcv.src_ip), msg->rcv.src_port, tcp_con_lifetime); if (!c) { LM_ERR("no corresponding TLS/TCP connection found." " This should not happen... return -1\n"); return -1; } LM_DBG("corresponding TLS/TCP connection found. s=%d, fd=%d, id=%d\n", c->s, c->fd, c->id); if (!c->extra_data) { LM_ERR("no extra_data specified in TLS/TCP connection found." " This should not happen... return -1\n"); tcpconn_put(c); return -1; } ssl = (SSL *) c->extra_data; ssl_verify = SSL_get_verify_result(ssl); if ( ssl_verify != X509_V_OK ) { LM_WARN("verification of presented certificate failed... return -1\n"); tcpconn_put(c); return -1; } /* now, we have only valid peer certificates or peers without certificates. * Thus we have to check for the existence of a peer certificate */ x509_cert = SSL_get_peer_certificate(ssl); if ( x509_cert == NULL ) { LM_WARN("tlsops:is_peer_verified: WARNING: peer did not presented " "a certificate. Thus it could not be verified... return -1\n"); tcpconn_put(c); return -1; } X509_free(x509_cert); tcpconn_put(c); LM_DBG("tlsops:is_peer_verified: peer is successfuly verified" "...done\n"); return 1; }
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; }