2016-02-17 17:19:57 -08:00
|
|
|
#include <assert.h>
|
|
|
|
|
#include <errno.h>
|
2016-03-05 22:54:26 -08:00
|
|
|
#include <netdb.h>
|
|
|
|
|
#include <stdlib.h>
|
2016-02-17 17:19:57 -08:00
|
|
|
#include <string.h>
|
2016-03-05 22:54:26 -08:00
|
|
|
#include <sys/socket.h>
|
2016-03-07 15:19:00 -08:00
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/types.h>
|
2016-03-08 22:00:08 -08:00
|
|
|
#include <unistd.h>
|
2016-02-17 17:19:57 -08:00
|
|
|
|
2016-02-27 12:41:56 -08:00
|
|
|
#include "buf.h"
|
2016-02-29 17:12:06 -08:00
|
|
|
#include "flow.h"
|
2016-03-05 22:54:26 -08:00
|
|
|
#include "log.h"
|
2016-03-08 11:26:39 -08:00
|
|
|
#include "opts.h"
|
2016-02-22 16:49:43 -08:00
|
|
|
#include "peer.h"
|
2016-03-08 11:26:39 -08:00
|
|
|
#include "receive.h"
|
2016-02-23 10:46:40 -08:00
|
|
|
#include "resolve.h"
|
2016-03-08 11:26:39 -08:00
|
|
|
#include "send.h"
|
|
|
|
|
#include "send_receive.h"
|
2016-02-21 16:47:27 -08:00
|
|
|
#include "wakeup.h"
|
2016-02-22 16:27:44 -08:00
|
|
|
#include "uuid.h"
|
|
|
|
|
|
2016-02-17 17:19:57 -08:00
|
|
|
#include "outgoing.h"
|
|
|
|
|
|
|
|
|
|
struct outgoing {
|
|
|
|
|
struct peer peer;
|
2016-02-25 23:37:37 -08:00
|
|
|
uint8_t id[UUID_LEN];
|
2016-02-21 13:57:35 -08:00
|
|
|
char *node;
|
|
|
|
|
char *service;
|
2016-02-17 17:19:57 -08:00
|
|
|
struct addrinfo *addrs;
|
|
|
|
|
struct addrinfo *addr;
|
2016-02-21 16:47:27 -08:00
|
|
|
uint32_t attempt;
|
2016-02-29 17:12:06 -08:00
|
|
|
struct flow *flow;
|
2016-02-17 17:19:57 -08:00
|
|
|
void *passthrough;
|
2016-02-26 10:30:18 -08:00
|
|
|
struct list_head outgoing_list;
|
2016-02-17 17:19:57 -08:00
|
|
|
};
|
|
|
|
|
|
2016-02-26 10:30:18 -08:00
|
|
|
static struct list_head outgoing_head = LIST_HEAD_INIT(outgoing_head);
|
2016-03-08 11:26:39 -08:00
|
|
|
static opts_group outgoing_opts;
|
2016-02-21 13:57:35 -08:00
|
|
|
|
2016-03-07 17:02:24 -08:00
|
|
|
static char log_module = 'O';
|
|
|
|
|
|
2016-02-17 17:19:57 -08:00
|
|
|
static void outgoing_connect_result(struct outgoing *, int);
|
2016-02-17 22:14:04 -08:00
|
|
|
static void outgoing_resolve(struct outgoing *);
|
2016-02-21 16:47:27 -08:00
|
|
|
static void outgoing_resolve_wrapper(struct peer *);
|
|
|
|
|
|
|
|
|
|
static void outgoing_retry(struct outgoing *outgoing) {
|
2016-03-08 22:42:15 -08:00
|
|
|
outgoing->peer.fd = -1;
|
2016-02-28 15:53:55 -08:00
|
|
|
uint32_t delay = wakeup_get_retry_delay_ms(++outgoing->attempt);
|
2016-03-07 17:02:24 -08:00
|
|
|
LOG(outgoing->id, "Will retry in %ds", delay / 1000);
|
2016-02-21 16:47:27 -08:00
|
|
|
outgoing->peer.event_handler = outgoing_resolve_wrapper;
|
2016-03-08 20:41:00 -08:00
|
|
|
wakeup_add(&outgoing->peer, delay);
|
2016-02-21 16:47:27 -08:00
|
|
|
}
|
2016-02-17 17:19:57 -08:00
|
|
|
|
|
|
|
|
static void outgoing_connect_next(struct outgoing *outgoing) {
|
|
|
|
|
if (outgoing->addr == NULL) {
|
|
|
|
|
freeaddrinfo(outgoing->addrs);
|
2016-03-07 17:02:24 -08:00
|
|
|
LOG(outgoing->id, "Can't connect to any addresses of %s/%s", outgoing->node, outgoing->service);
|
2016-02-21 16:47:27 -08:00
|
|
|
outgoing_retry(outgoing);
|
2016-02-17 17:19:57 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
|
|
|
|
|
assert(getnameinfo(outgoing->addr->ai_addr, outgoing->addr->ai_addrlen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0);
|
2016-03-07 17:02:24 -08:00
|
|
|
LOG(outgoing->id, "Connecting to %s/%s...", hbuf, sbuf);
|
2016-02-17 17:19:57 -08:00
|
|
|
|
2016-02-23 12:00:17 -08:00
|
|
|
outgoing->peer.fd = socket(outgoing->addr->ai_family, outgoing->addr->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC, outgoing->addr->ai_protocol);
|
2016-02-17 17:19:57 -08:00
|
|
|
assert(outgoing->peer.fd >= 0);
|
|
|
|
|
|
2016-02-27 12:48:01 -08:00
|
|
|
struct buf buf = BUF_INIT, *buf_ptr = &buf;
|
2016-03-02 22:25:56 -08:00
|
|
|
flow_get_hello(outgoing->flow, &buf_ptr, outgoing->passthrough);
|
2016-02-28 22:34:53 -08:00
|
|
|
ssize_t result = sendto(outgoing->peer.fd, buf_at(buf_ptr, 0), buf_ptr->length, MSG_FASTOPEN, outgoing->addr->ai_addr, outgoing->addr->ai_addrlen);
|
|
|
|
|
outgoing_connect_result(outgoing, result == (ssize_t) buf_ptr->length ? EINPROGRESS : errno);
|
2016-02-17 17:19:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void outgoing_connect_handler(struct peer *peer) {
|
2016-03-08 20:47:22 -08:00
|
|
|
struct outgoing *outgoing = container_of(peer, struct outgoing, peer);
|
2016-02-17 17:19:57 -08:00
|
|
|
|
2016-02-17 22:14:04 -08:00
|
|
|
peer_epoll_del(&outgoing->peer);
|
2016-02-17 17:19:57 -08:00
|
|
|
|
|
|
|
|
int error;
|
|
|
|
|
socklen_t len = sizeof(error);
|
|
|
|
|
assert(getsockopt(outgoing->peer.fd, SOL_SOCKET, SO_ERROR, &error, &len) == 0);
|
|
|
|
|
outgoing_connect_result(outgoing, error);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-17 22:14:04 -08:00
|
|
|
static void outgoing_disconnect_handler(struct peer *peer) {
|
2016-03-08 20:47:22 -08:00
|
|
|
struct outgoing *outgoing = container_of(peer, struct outgoing, peer);
|
2016-03-07 17:02:24 -08:00
|
|
|
LOG(outgoing->id, "Peer disconnected; reconnecting...");
|
2016-02-24 20:25:31 -08:00
|
|
|
outgoing_retry(outgoing);
|
2016-02-17 22:14:04 -08:00
|
|
|
}
|
|
|
|
|
|
2016-02-17 17:19:57 -08:00
|
|
|
static void outgoing_connect_result(struct outgoing *outgoing, int result) {
|
|
|
|
|
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
|
|
|
|
|
assert(getnameinfo(outgoing->addr->ai_addr, outgoing->addr->ai_addrlen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0);
|
|
|
|
|
switch (result) {
|
|
|
|
|
case 0:
|
2016-03-07 17:02:24 -08:00
|
|
|
LOG(outgoing->id, "Connected to %s/%s", hbuf, sbuf);
|
2016-02-17 17:19:57 -08:00
|
|
|
freeaddrinfo(outgoing->addrs);
|
2016-02-21 16:47:27 -08:00
|
|
|
outgoing->attempt = 0;
|
2016-02-24 20:15:09 -08:00
|
|
|
int fd = outgoing->peer.fd;
|
|
|
|
|
outgoing->peer.fd = -1;
|
2016-02-17 22:14:04 -08:00
|
|
|
outgoing->peer.event_handler = outgoing_disconnect_handler;
|
2016-03-02 21:51:30 -08:00
|
|
|
flow_socket_ready(fd, outgoing->flow);
|
|
|
|
|
flow_socket_connected(fd, outgoing->flow);
|
2016-03-08 20:41:00 -08:00
|
|
|
flow_new(fd, outgoing->flow, outgoing->passthrough, &outgoing->peer);
|
2016-02-17 17:19:57 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case EINPROGRESS:
|
|
|
|
|
outgoing->peer.event_handler = outgoing_connect_handler;
|
2016-03-08 20:41:00 -08:00
|
|
|
peer_epoll_add(&outgoing->peer, EPOLLOUT);
|
2016-02-17 17:19:57 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2016-03-07 17:02:24 -08:00
|
|
|
LOG(outgoing->id, "Can't connect to %s/%s: %s", hbuf, sbuf, strerror(result));
|
2016-03-08 21:53:57 -08:00
|
|
|
assert(!close(outgoing->peer.fd));
|
2016-02-17 17:19:57 -08:00
|
|
|
outgoing->addr = outgoing->addr->ai_next;
|
|
|
|
|
// Tail recursion :/
|
|
|
|
|
outgoing_connect_next(outgoing);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-23 10:46:40 -08:00
|
|
|
static void outgoing_resolve_handler(struct peer *peer) {
|
2016-03-08 20:47:22 -08:00
|
|
|
struct outgoing *outgoing = container_of(peer, struct outgoing, peer);
|
2016-02-29 22:37:41 -08:00
|
|
|
int err = resolve_result(peer, &outgoing->addrs);
|
|
|
|
|
if (err) {
|
2016-03-07 17:02:24 -08:00
|
|
|
LOG(outgoing->id, "Failed to resolve %s/%s: %s", outgoing->node, outgoing->service, gai_strerror(err));
|
2016-02-29 22:37:41 -08:00
|
|
|
outgoing_retry(outgoing);
|
|
|
|
|
} else {
|
2016-02-23 10:46:40 -08:00
|
|
|
outgoing->addr = outgoing->addrs;
|
|
|
|
|
outgoing_connect_next(outgoing);
|
2016-02-17 17:19:57 -08:00
|
|
|
}
|
2016-02-23 10:46:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void outgoing_resolve(struct outgoing *outgoing) {
|
2016-03-07 17:02:24 -08:00
|
|
|
LOG(outgoing->id, "Resolving %s/%s...", outgoing->node, outgoing->service);
|
2016-02-23 10:46:40 -08:00
|
|
|
outgoing->peer.event_handler = outgoing_resolve_handler;
|
2016-03-08 20:41:00 -08:00
|
|
|
resolve(&outgoing->peer, outgoing->node, outgoing->service, 0);
|
2016-02-17 17:19:57 -08:00
|
|
|
}
|
|
|
|
|
|
2016-02-21 16:47:27 -08:00
|
|
|
static void outgoing_resolve_wrapper(struct peer *peer) {
|
2016-03-08 20:47:22 -08:00
|
|
|
outgoing_resolve(container_of(peer, struct outgoing, peer));
|
2016-02-21 16:47:27 -08:00
|
|
|
}
|
|
|
|
|
|
2016-02-21 13:57:35 -08:00
|
|
|
static void outgoing_del(struct outgoing *outgoing) {
|
2016-03-02 19:20:25 -08:00
|
|
|
flow_ref_dec(outgoing->flow);
|
2016-03-08 20:41:00 -08:00
|
|
|
peer_close(&outgoing->peer);
|
2016-03-02 18:52:02 -08:00
|
|
|
list_del(&outgoing->outgoing_list);
|
2016-02-21 13:57:35 -08:00
|
|
|
free(outgoing->node);
|
|
|
|
|
free(outgoing->service);
|
|
|
|
|
free(outgoing);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-08 11:26:39 -08:00
|
|
|
static bool outgoing_add(const char *host_port, struct flow *flow, void *passthrough) {
|
|
|
|
|
char *host = opts_split(&host_port, '/');
|
|
|
|
|
if (!host) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outgoing_new(host, host_port, flow, passthrough);
|
|
|
|
|
free(host);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool outgoing_connect_receive(const char *arg) {
|
|
|
|
|
return outgoing_add(arg, receive_flow, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool outgoing_connect_send(const char *arg) {
|
2016-03-08 12:22:46 -08:00
|
|
|
return send_add(outgoing_add, send_flow, arg);
|
2016-03-08 11:26:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool outgoing_connect_send_receive(const char *arg) {
|
2016-03-08 12:22:46 -08:00
|
|
|
return send_add(outgoing_add, send_receive_flow, arg);
|
2016-03-08 11:26:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void outgoing_opts_add() {
|
|
|
|
|
opts_add("connect-receive", "HOST/PORT", outgoing_connect_receive, outgoing_opts);
|
|
|
|
|
opts_add("connect-send", "FORMAT=HOST/PORT", outgoing_connect_send, outgoing_opts);
|
|
|
|
|
opts_add("connect-send-receive", "FORMAT=HOST/PORT", outgoing_connect_send_receive, outgoing_opts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void outgoing_init() {
|
|
|
|
|
opts_call(outgoing_opts);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-21 13:57:35 -08:00
|
|
|
void outgoing_cleanup() {
|
2016-02-26 10:30:18 -08:00
|
|
|
struct outgoing *iter, *next;
|
|
|
|
|
list_for_each_entry_safe(iter, next, &outgoing_head, outgoing_list) {
|
2016-02-21 13:57:35 -08:00
|
|
|
outgoing_del(iter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-07 11:26:25 -08:00
|
|
|
void outgoing_new(const char *node, const char *service, struct flow *flow, void *passthrough) {
|
2016-03-02 19:20:25 -08:00
|
|
|
flow_ref_inc(flow);
|
2016-02-25 16:17:25 -08:00
|
|
|
|
2016-02-17 17:19:57 -08:00
|
|
|
struct outgoing *outgoing = malloc(sizeof(*outgoing));
|
|
|
|
|
uuid_gen(outgoing->id);
|
2016-02-20 23:22:00 -08:00
|
|
|
outgoing->node = strdup(node);
|
|
|
|
|
outgoing->service = strdup(service);
|
2016-02-21 16:47:27 -08:00
|
|
|
outgoing->attempt = 0;
|
2016-02-29 17:12:06 -08:00
|
|
|
outgoing->flow = flow;
|
2016-02-17 17:19:57 -08:00
|
|
|
outgoing->passthrough = passthrough;
|
2016-02-21 13:57:35 -08:00
|
|
|
|
2016-02-26 10:30:18 -08:00
|
|
|
list_add(&outgoing->outgoing_list, &outgoing_head);
|
2016-02-21 13:57:35 -08:00
|
|
|
|
2016-02-17 17:19:57 -08:00
|
|
|
outgoing_resolve(outgoing);
|
|
|
|
|
}
|