mirror of https://github.com/sipwise/rtpengine.git
parent
135a95ef02
commit
e72f7e4b0f
@ -0,0 +1,194 @@
|
||||
#include "ng_client.h"
|
||||
#include "media_socket.h"
|
||||
|
||||
struct endpoint_sockets {
|
||||
endpoint_t dst;
|
||||
socket_slist *sockets;
|
||||
};
|
||||
|
||||
static void socket_free(socket_t *s) {
|
||||
close_socket(s);
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
static void endpoint_socket_free(struct endpoint_sockets *es) {
|
||||
t_slist_free_full(es->sockets, socket_free);
|
||||
g_free(es);
|
||||
}
|
||||
|
||||
TYPED_GHASHTABLE(endpoint_socket_ht, endpoint_t, struct endpoint_sockets,
|
||||
endpoint_hash, endpoint_eq,
|
||||
NULL, endpoint_socket_free)
|
||||
|
||||
|
||||
static endpoint_socket_ht ng_client_endpoints;
|
||||
static rwlock_t ng_client_endpoints_lock;
|
||||
|
||||
|
||||
void ng_client_init(void) {
|
||||
ng_client_endpoints = endpoint_socket_ht_new();
|
||||
}
|
||||
|
||||
void ng_client_cleanup(void) {
|
||||
t_hash_table_destroy(ng_client_endpoints);
|
||||
}
|
||||
|
||||
|
||||
static struct endpoint_sockets *ng_client_get_entry(const endpoint_t *dst) {
|
||||
struct endpoint_sockets *es;
|
||||
|
||||
// quick check for existing entry
|
||||
{
|
||||
RWLOCK_R(&ng_client_endpoints_lock);
|
||||
es = t_hash_table_lookup(ng_client_endpoints, dst);
|
||||
}
|
||||
|
||||
if (es)
|
||||
return es;
|
||||
|
||||
// we have to create one
|
||||
es = g_new0(__typeof(*es), 1);
|
||||
es->dst = *dst;
|
||||
|
||||
RWLOCK_W(&ng_client_endpoints_lock);
|
||||
// ... but someone may have beaten us to it
|
||||
__auto_type es2 = t_hash_table_lookup(ng_client_endpoints, dst);
|
||||
if (es2) {
|
||||
// lost the race
|
||||
g_free(es);
|
||||
es = es2;
|
||||
}
|
||||
else
|
||||
t_hash_table_insert(ng_client_endpoints, &es->dst, es);
|
||||
|
||||
return es;
|
||||
}
|
||||
|
||||
static socket_slist *ng_client_get_socket(struct endpoint_sockets *es) {
|
||||
// see if we can grab a socket
|
||||
socket_slist *link = __atomic_load_n(&es->sockets, __ATOMIC_SEQ_CST);
|
||||
bool success = false;
|
||||
while (link && !success)
|
||||
success = __atomic_compare_exchange_n(&es->sockets, &link, link->next,
|
||||
false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
||||
|
||||
if (link) {
|
||||
link->next = NULL;
|
||||
return link;
|
||||
}
|
||||
|
||||
// no socket available, we need to create one
|
||||
link = g_new0(socket_slist, 1);
|
||||
__auto_type sock = link->data = g_new0(socket_t, 1);
|
||||
if (!connect_socket(sock, SOCK_DGRAM, &es->dst)) {
|
||||
// oops...
|
||||
ilog(LOG_ERR, "Failed to create or connect socket to remote NG peer (%s): %s",
|
||||
endpoint_print_buf(&es->dst),
|
||||
strerror(errno));
|
||||
g_free(link->data);
|
||||
g_free(link);
|
||||
return NULL;
|
||||
}
|
||||
socket_getsockname(sock);
|
||||
interfaces_exclude_port(&sock->local);
|
||||
socket_rcvtimeout(sock, rtpe_config.ng_client_timeout);
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
static void ng_client_put_socket(struct endpoint_sockets *es, socket_slist *link) {
|
||||
link->next = __atomic_load_n(&es->sockets, __ATOMIC_SEQ_CST);
|
||||
bool success;
|
||||
do
|
||||
success = __atomic_compare_exchange_n(&es->sockets, &link->next, link,
|
||||
false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
||||
while (!success);
|
||||
}
|
||||
|
||||
bencode_item_t *ng_client_request(const endpoint_t *dst, const str *req, bencode_buffer_t *rbuf) {
|
||||
__auto_type es = ng_client_get_entry(dst);
|
||||
__auto_type link = ng_client_get_socket(es);
|
||||
if (!link)
|
||||
return NULL;
|
||||
|
||||
__auto_type sock = link->data;
|
||||
|
||||
// construct message
|
||||
char cookie[17];
|
||||
rand_hex_str(cookie, 8);
|
||||
cookie[16] = ' ';
|
||||
|
||||
struct iovec iov[2] = {
|
||||
{
|
||||
.iov_base = cookie,
|
||||
.iov_len = sizeof(cookie),
|
||||
},
|
||||
{
|
||||
.iov_base = req->s,
|
||||
.iov_len = req->len,
|
||||
},
|
||||
};
|
||||
|
||||
ilog(LOG_DEBUG, "Sending NG request to %s: '" STR_FORMAT "'",
|
||||
endpoint_print_buf(dst),
|
||||
STR_FMT(req));
|
||||
|
||||
static const size_t buflen = 4096;
|
||||
char *buf = bencode_buffer_alloc(rbuf, buflen);
|
||||
ssize_t len = 0;
|
||||
|
||||
for (unsigned int try = 0; try < rtpe_config.ng_client_retries; try++) {
|
||||
ilog(LOG_DEBUG, "Attempt #%u sending NG request", try + 1);
|
||||
|
||||
ssize_t ret = socket_sendiov(sock, iov, G_N_ELEMENTS(iov), NULL, NULL);
|
||||
if (ret <= 0)
|
||||
goto err;
|
||||
|
||||
// receive the response
|
||||
len = socket_recvfrom(sock, buf, buflen, NULL);
|
||||
if (len > 0)
|
||||
ilog(LOG_DEBUG, "Received response from NG peer (%s): '%.*s'",
|
||||
endpoint_print_buf(dst),
|
||||
(int) len, buf);
|
||||
if (len == buflen)
|
||||
ilog(LOG_WARN, "Possibly truncated response from remote NG peer (%s)",
|
||||
endpoint_print_buf(dst));
|
||||
if (len > sizeof(cookie) && memcmp(buf, cookie, sizeof(cookie)) == 0)
|
||||
break;
|
||||
|
||||
if (len < 0)
|
||||
ilog(LOG_WARN, "Error reading from socket from remote NG peer (%s): %s",
|
||||
endpoint_print_buf(dst),
|
||||
strerror(errno));
|
||||
else if (len == 0)
|
||||
ilog(LOG_WARN, "EOF while reading from socket from remote NG peer (%s)",
|
||||
endpoint_print_buf(dst));
|
||||
else
|
||||
ilog(LOG_WARN, "Short packet or mismatched cookie while reading from socket "
|
||||
"from remote NG peer (%s)",
|
||||
endpoint_print_buf(dst));
|
||||
}
|
||||
|
||||
if (len <= 0)
|
||||
goto err;
|
||||
|
||||
ilog(LOG_DEBUG, "Received valid NG response: '%.*s'", (int) len, buf);
|
||||
|
||||
bencode_item_t *ret = bencode_decode_expect(rbuf, buf + sizeof(cookie), len - sizeof(cookie),
|
||||
BENCODE_DICTIONARY);
|
||||
if (!ret) {
|
||||
errno = EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ng_client_put_socket(es, link);
|
||||
return ret;
|
||||
|
||||
err:
|
||||
ilog(LOG_ERR, "Error communicating with remote NG peer (%s): %s",
|
||||
endpoint_print_buf(&sock->remote),
|
||||
strerror(errno));
|
||||
|
||||
ng_client_put_socket(es, link);
|
||||
return NULL;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
#ifndef _NG_CLIENT_H_
|
||||
#define _NG_CLIENT_H_
|
||||
|
||||
#include "types.h"
|
||||
#include "bencode.h"
|
||||
|
||||
void ng_client_init(void);
|
||||
void ng_client_cleanup(void);
|
||||
|
||||
bencode_item_t *ng_client_request(const endpoint_t *dst, const str *req, bencode_buffer_t *);
|
||||
|
||||
#endif
|
Loading…
Reference in new issue