Lindenii Project Forge
Warning: Due to various recent migrations, viewing non-HEAD refs may be broken.
/ev/dial/ip.ha (raw)
// License: MPL-2.0
// (c) 2021-2023 Drew DeVault <sir@cmpwn.com>
// (c) 2021 Bor Grošelj Simić <bor.groseljsimic@telemach.net>
// (c) 2021 Ember Sawady <ecs@d2evs.net>
//
// Provides default dialers for tcp and udp
use errors;
use ev;
use math::random;
use net::ip;
use net::tcp;
use net::udp;
use net;
use time;
type tcp_dialer = struct {
loop: *ev::loop,
cb: *dialcb,
user: nullable *opaque,
req: ev::req,
ip: []ip::addr,
n: uint,
port: u16,
};
fn dial_tcp(
loop: *ev::loop,
addr: str,
service: str,
cb: *dialcb,
user: nullable *opaque,
) (ev::req | error) = {
let state = alloc(tcp_dialer {
loop = loop,
cb = cb,
user = user,
...
})?;
match (resolve(loop, "tcp", addr,
service, &dial_tcp_resolvecb, state)) {
case let req: ev::req =>
state.req = req;
return ev::mkreq(&dial_tcp_cancel, state);
case let e: error =>
free(state);
return e;
};
};
fn dial_tcp_resolvecb(
user: nullable *opaque,
r: (([]ip::addr, u16) | error),
) (void | nomem) = {
let state = user: *tcp_dialer;
state.req = ev::req { ... };
const (ip, port) = match (r) {
case let r: ([]ip::addr, u16) =>
yield r;
case let err: error =>
dial_tcp_complete(state, err)?;
return;
};
state.ip = ip;
state.port = port;
dial_tcp_connect(state)?;
};
fn dial_tcp_connect(state: *tcp_dialer) (void | nomem) = {
const req = match (ev::connect_tcp(state.loop,
&dial_tcp_connectcb,
state.ip[state.n], state.port,
state)) {
case let err: (net::error | errors::error) =>
dial_tcp_complete(state, err)?;
return;
case let req: ev::req =>
yield req;
};
state.req = req;
};
fn dial_tcp_connectcb(
r: (*ev::file | net::error),
user: nullable *opaque,
) (void | nomem) = {
let state = user: *tcp_dialer;
match (r) {
case let sock: *ev::file =>
ev::setuser(sock, null);
dial_tcp_complete(state, sock)?;
return;
case let err: net::error =>
if (err is errors::netunreachable) {
state.n += 1;
if (state.n < len(state.ip)) {
dial_tcp_connect(state)?;
return;
};
};
dial_tcp_complete(state, err)?;
};
};
fn dial_tcp_cancel(req: *ev::req) void = {
let state = req.user: *tcp_dialer;
ev::cancel(&state.req);
free(state.ip);
free(state);
};
fn dial_tcp_complete(state: *tcp_dialer, r: (*ev::file | error)) (void | nomem) = {
const cb = state.cb;
const user = state.user;
free(state.ip);
free(state);
cb(user, r)?;
};
type udp_dialer = struct {
loop: *ev::loop,
cb: *dialcb,
user: nullable *opaque,
req: ev::req,
ip: []ip::addr,
n: uint,
port: u16,
};
fn dial_udp(
loop: *ev::loop,
addr: str,
service: str,
cb: *dialcb,
user: nullable *opaque,
) (ev::req | error) = {
let state = alloc(udp_dialer {
loop = loop,
cb = cb,
user = user,
...
})?;
match (resolve(loop, "udp", addr,
service, &dial_udp_resolvecb, state)) {
case let req: ev::req =>
state.req = req;
return ev::mkreq(&dial_udp_cancel, state);
case let e: error =>
free(state);
return e;
};
};
fn dial_udp_resolvecb(
user: nullable *opaque,
r: (([]ip::addr, u16) | error),
) (void | nomem) = {
let state = user: *udp_dialer;
state.req = ev::req { ... };
const (ip, port) = match (r) {
case let r: ([]ip::addr, u16) =>
yield r;
case let err: error =>
dial_udp_complete(state, err)?;
return;
};
state.ip = ip;
state.port = port;
dial_udp_connect(state)?;
};
fn dial_udp_connect(state: *udp_dialer) (void | nomem) = {
for (true) {
match (ev::connect_udp(state.loop, state.ip[state.n], state.port)) {
case let err: net::error =>
if (err is errors::netunreachable) {
state.n += 1;
if (state.n < len(state.ip)) {
continue;
};
};
dial_udp_complete(state, err)?;
break;
case let err: errors::error =>
dial_udp_complete(state, err)?;
if (err is errors::netunreachable) {
state.n += 1;
if (state.n < len(state.ip)) {
continue;
};
};
break;
case let sock: *ev::file =>
dial_udp_complete(state, sock)?;
break;
};
};
};
fn dial_udp_cancel(req: *ev::req) void = {
let state = req.user: *udp_dialer;
ev::cancel(&state.req);
free(state.ip);
free(state);
};
fn dial_udp_complete(state: *udp_dialer, r: (*ev::file | error)) (void | nomem) = {
const cb = state.cb;
const user = state.user;
free(state.ip);
free(state);
cb(user, r)?;
};