/*! \brief finds a connection, if id=0 uses the ip addr & port (host byte order) * \note WARNING: unprotected (locks) use tcpconn_get unless you really * know what you are doing */ struct tcp_connection* _tcpconn_find(int id, struct ip_addr* ip, int port) { struct tcp_connection *c; struct tcp_conn_alias* a; unsigned hash; #ifdef EXTRA_DEBUG LM_DBG("%d port %d\n",id, port); if (ip) print_ip("tcpconn_find: ip ", ip, "\n"); #endif if (id){ hash=tcp_id_hash(id); for (c=tcpconn_id_hash[hash]; c; c=c->id_next){ #ifdef EXTRA_DEBUG LM_DBG("c=%p, c->id=%d, port=%d\n",c, c->id, c->rcv.src_port); print_ip("ip=", &c->rcv.src_ip, "\n"); #endif if ((id==c->id)&&(c->state!=S_CONN_BAD)) return c; } }else if (ip){ hash=tcp_addr_hash(ip, port); for (a=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 if ( (a->parent->state!=S_CONN_BAD) && (port==a->port) && (ip_addr_cmp(ip, &a->parent->rcv.src_ip)) ) return a->parent; } } return 0; }
/* finds a connection, if id=0 uses the ip addr & port (host byte order) * WARNING: unprotected (locks) use tcpconn_get unless you really * know what you are doing */ struct tcp_connection* _tcpconn_find(int id, struct ip_addr* ip, int port) { struct tcp_connection *c; unsigned hash; #ifdef EXTRA_DEBUG DBG("tcpconn_find: %d port %d\n",id, port); print_ip("tcpconn_find: ip ", ip, "\n"); #endif if (id){ hash=tcp_id_hash(id); for (c=tcpconn_id_hash[hash]; c; c=c->id_next){ #ifdef EXTRA_DEBUG DBG("c=%p, c->id=%d, port=%d\n",c, c->id, c->rcv.src_port); print_ip("ip=", &c->rcv.src_ip, "\n"); #endif if ((id==c->id)&&(c->state!=S_CONN_BAD)) return c; } }else if (ip){ hash=tcp_addr_hash(ip, port); for (c=tcpconn_addr_hash[hash]; c; c=c->next){ #ifdef EXTRA_DEBUG DBG("c=%p, c->id=%d, port=%d\n",c, c->id, c->rcv.src_port); print_ip("ip=",&c->rcv.src_ip,"\n"); #endif if ( (c->state!=S_CONN_BAD) && (port==c->rcv.src_port) && (ip_addr_cmp(ip, &c->rcv.src_ip)) ) return c; } } return 0; }
struct tcp_connection* tcpconn_add(struct tcp_connection *c) { unsigned hash; if (c){ TCPCONN_LOCK; /* add it at the begining of the list*/ hash=tcp_id_hash(c->id); c->id_hash=hash; tcpconn_listadd(tcpconn_id_hash[hash], c, id_next, id_prev); hash=tcp_addr_hash(&c->rcv.src_ip, c->rcv.src_port); /* set the first alias */ c->con_aliases[0].port=c->rcv.src_port; c->con_aliases[0].hash=hash; c->con_aliases[0].parent=c; tcpconn_listadd(tcpconn_aliases_hash[hash], &c->con_aliases[0], next, prev); c->aliases++; TCPCONN_UNLOCK; LM_DBG("hashes: %d, %d\n", hash, c->id_hash); return c; }else{ LM_CRIT("null connection pointer\n"); return 0; } }
/* 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; }
struct tcp_connection* tcpconn_add(struct tcp_connection *c) { unsigned hash; if (c){ TCPCONN_LOCK; /* add it at the begining of the list*/ hash=tcp_addr_hash(&c->rcv.src_ip, c->rcv.src_port); c->addr_hash=hash; tcpconn_listadd(tcpconn_addr_hash[hash], c, next, prev); hash=tcp_id_hash(c->id); c->id_hash=hash; tcpconn_listadd(tcpconn_id_hash[hash], c, id_next, id_prev); TCPCONN_UNLOCK; DBG("tcpconn_add: hashes: %d, %d\n", c->addr_hash, c->id_hash); return c; }else{ LOG(L_CRIT, "tcpconn_add: BUG: null connection pointer\n"); return 0; } }
/*! \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; }