/* add port as an alias for the "id" connection * returns 0 on success,-1 on failure */ int tcpconn_add_alias(int id, int port, int proto) { struct tcp_connection* c; unsigned hash; struct tcp_conn_alias* a; a=0; /* fix the port */ port=port?port:((proto==PROTO_TLS)?SIPS_PORT:SIP_PORT); TCPCONN_LOCK; /* check if alias already exists */ c=_tcpconn_find(id, 0, 0); if (c){ hash=tcp_addr_hash(&c->rcv.src_ip, port); /* search the aliases for an already existing one */ for (a=tcpconn_aliases_hash[hash]; a; a=a->next){ if ( (a->parent->state!=S_CONN_BAD) && (port==a->port) && (ip_addr_cmp(&c->rcv.src_ip, &a->parent->rcv.src_ip)) ){ /* found */ if (a->parent!=c) goto error_sec; else goto ok; } } if (c->aliases>=TCP_CON_MAX_ALIASES) goto error_aliases; c->con_aliases[c->aliases].parent=c; c->con_aliases[c->aliases].port=port; c->con_aliases[c->aliases].hash=hash; tcpconn_listadd(tcpconn_aliases_hash[hash], &c->con_aliases[c->aliases], next, prev); c->aliases++; }else goto error_not_found; ok: TCPCONN_UNLOCK; #ifdef EXTRA_DEBUG if (a) DBG("tcpconn_add_alias: alias already present\n"); else DBG("tcpconn_add_alias: alias port %d for hash %d, id %d\n", port, hash, c->id); #endif return 0; error_aliases: TCPCONN_UNLOCK; LOG(L_ERR, "ERROR: tcpconn_add_alias: too many aliases for connection %p" " (%d)\n", c, c->id); return -1; error_not_found: TCPCONN_UNLOCK; LOG(L_ERR, "ERROR: tcpconn_add_alias: no connection found for id %d\n",id); return -1; error_sec: TCPCONN_UNLOCK; LOG(L_ERR, "ERROR: tcpconn_add_alias: possible port hijack attempt\n"); LOG(L_ERR, "ERROR: tcpconn_add_alias: alias already present and points" " to another connection (%d : %d and %d : %d)\n", a->parent->id, port, c->id, port); return -1; }
/*! \brief add port as an alias for the "id" connection * \return 0 on success,-1 on failure */ int tcpconn_add_alias(int id, int port, int proto) { struct tcp_connection* c; unsigned hash; struct tcp_conn_alias* a; a=0; /* fix the port */ port=port ? port : protos[proto].default_port ; TCPCONN_LOCK(id); /* check if alias already exists */ c=_tcpconn_find(id); if (c){ hash=tcp_addr_hash(&c->rcv.src_ip, port); /* search the aliases for an already existing one */ for (a=TCP_PART(id).tcpconn_aliases_hash[hash]; a; a=a->next) { if (a->parent->state != S_CONN_BAD && port == a->port && proto == a->parent->type && ip_addr_cmp(&c->rcv.src_ip, &a->parent->rcv.src_ip)) { /* found */ if (a->parent!=c) goto error_sec; else goto ok; } } if (c->aliases>=TCP_CON_MAX_ALIASES) goto error_aliases; c->con_aliases[c->aliases].parent=c; c->con_aliases[c->aliases].port=port; c->con_aliases[c->aliases].hash=hash; tcpconn_listadd(TCP_PART(id).tcpconn_aliases_hash[hash], &c->con_aliases[c->aliases], next, prev); c->aliases++; }else goto error_not_found; ok: TCPCONN_UNLOCK(id); #ifdef EXTRA_DEBUG if (a) LM_DBG("alias already present\n"); else LM_DBG("alias port %d for hash %d, id %d\n", port, hash, id); #endif return 0; error_aliases: TCPCONN_UNLOCK(id); LM_ERR("too many aliases for connection %p (%d)\n", c, id); return -1; error_not_found: TCPCONN_UNLOCK(id); LM_ERR("no connection found for id %d\n",id); return -1; error_sec: LM_WARN("possible port hijack attempt\n"); LM_WARN("alias already present and points to another connection " "(%d : %d and %d : %d)\n", a->parent->id, port, id, port); TCPCONN_UNLOCK(id); return -1; }
/* _tcpconn_find with locks and timeout */ struct tcp_connection* tcpconn_get(int id, struct ip_addr* ip, int port, int timeout) { struct tcp_connection* c; TCPCONN_LOCK; c=_tcpconn_find(id, ip, port); if (c){ c->refcnt++; c->timeout=get_ticks()+timeout; } TCPCONN_UNLOCK; return c; }
/* used to tune the tcp_connection attributes - not to be used inside the network layer, but onlu from the above layer (otherwise we may end up in strange deadlocks!) */ int tcp_conn_fcntl(struct receive_info *rcv, int attr, void *value) { struct tcp_connection *con; switch (attr) { case DST_FCNTL_SET_LIFETIME: /* set connection timeout */ TCPCONN_LOCK(rcv->proto_reserved1); con =_tcpconn_find(rcv->proto_reserved1); if (!con) { LM_ERR("Strange, tcp conn not found (id=%d)\n", rcv->proto_reserved1); } else { tcp_conn_set_lifetime( con, (int)(long)(value)); } TCPCONN_UNLOCK(rcv->proto_reserved1); return 0; default: LM_ERR("unsupported operation %d on conn\n",attr); return -1; } return -1; }
/*! \brief _tcpconn_find with locks and acquire fd */ int tcp_conn_get(int id, struct ip_addr* ip, int port, enum sip_protos proto, struct tcp_connection** conn, int* conn_fd) { struct tcp_connection* c; struct tcp_connection* tmp; struct tcp_conn_alias* a; unsigned hash; long response[2]; int part; int n; int fd; if (id) { part = id; TCPCONN_LOCK(part); if ( (c=_tcpconn_find(part))!=NULL ) goto found; TCPCONN_UNLOCK(part); } /* continue search based on IP address + port + transport */ #ifdef EXTRA_DEBUG LM_DBG("%d port %d\n",id, port); if (ip) print_ip("tcpconn_find: ip ", ip, "\n"); #endif if (ip){ hash=tcp_addr_hash(ip, port); for( part=0 ; part<TCP_PARTITION_SIZE ; part++ ) { TCPCONN_LOCK(part); for (a=TCP_PART(part).tcpconn_aliases_hash[hash]; a; a=a->next) { #ifdef EXTRA_DEBUG LM_DBG("a=%p, c=%p, c->id=%d, alias port= %d port=%d\n", a, a->parent, a->parent->id, a->port, a->parent->rcv.src_port); print_ip("ip=",&a->parent->rcv.src_ip,"\n"); #endif c = a->parent; if (c->state != S_CONN_BAD && port == a->port && proto == c->type && ip_addr_cmp(ip, &c->rcv.src_ip)) goto found; } TCPCONN_UNLOCK(part); } } /* not found */ *conn = NULL; if (conn_fd) *conn_fd = -1; return 0; found: c->refcnt++; TCPCONN_UNLOCK(part); LM_DBG("con found in state %d\n",c->state); if (c->state!=S_CONN_OK || conn_fd==NULL) { /* no need to acquired, just return the conn with an invalid fd */ *conn = c; if (conn_fd) *conn_fd = -1; return 1; } if (c->proc_id == process_no) { LM_DBG("tcp connection found (%p) already in this process ( %d ) , fd = %d\n", c, c->proc_id, c->fd); /* we already have the connection in this worker's reactor, no need to acquire FD */ *conn = c; *conn_fd = c->fd; return 1; } /* acquire the fd for this connection too */ LM_DBG("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){ LM_ERR("failed to get fd(write):%s (%d)\n", strerror(errno), errno); n=-1; goto error; } LM_DBG("c= %p, n=%d, Usock=%d\n", c, n, unix_tcp_sock); tmp = c; n=receive_fd(unix_tcp_sock, &c, sizeof(c), &fd, MSG_WAITALL); if (n<=0){ LM_ERR("failed to get fd(receive_fd):" " %s (%d)\n", strerror(errno), errno); n=-1; goto error; } 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 */ close(fd); goto error; } LM_DBG("after receive_fd: c= %p n=%d fd=%d\n",c, n, fd); *conn = c; *conn_fd = fd; return 1; error: tcpconn_put(c); *conn = NULL; *conn_fd = -1; return -1; }