terom@153: terom@153: #include "irc_net_internal.h" terom@177: #include "tcp.h" terom@180: #include "ssl.h" terom@153: #include "log.h" terom@153: terom@153: #include terom@153: #include terom@153: terom@154: void irc_net_disconnect (struct irc_net *net) terom@153: { terom@153: struct irc_chan *chan = NULL; terom@153: terom@153: // mark terom@153: net->connected = false; terom@153: terom@153: // destroy connection and set NULL terom@153: irc_conn_destroy(net->conn); terom@153: net->conn = NULL; terom@153: terom@153: // update channel state terom@153: TAILQ_FOREACH(chan, &net->channels, net_channels) { terom@153: // XXX: notify channel somehow terom@153: terom@153: } terom@153: terom@153: } terom@153: terom@153: /** terom@156: * We have succesfully established a connection to our server with the given transport, so create the irc_conn and bind terom@156: * it to us. terom@153: * terom@156: * If this fails, this will clean up any partial state, including \a transport. terom@153: */ terom@156: static err_t irc_net_connected (struct irc_net *net, transport_t *transport, struct error_info *err) terom@153: { terom@153: // mark state terom@153: net->connecting = false; terom@153: terom@153: // create the irc connection state terom@156: if (irc_conn_create(&net->conn, transport, &irc_net_conn_callbacks, net, err)) terom@153: goto error; terom@153: terom@153: // add our command handlers terom@153: if ((ERROR_CODE(err) = irc_conn_add_cmd_handlers (net->conn, irc_net_cmd_handlers, net))) terom@153: goto error; terom@153: terom@153: // register terom@153: if ((ERROR_CODE(err) = irc_conn_register(net->conn, &net->info.register_info))) terom@153: goto error; terom@153: terom@153: // great, we're alive now terom@153: net->connected = true; terom@153: net->connected_ts = time(NULL); terom@153: terom@153: return SUCCESS; terom@153: terom@153: error: terom@153: if (!net->conn) { terom@156: // cleanup transport ourselves terom@156: transport_destroy(transport); terom@153: terom@153: } else { terom@153: // cleanup the partial stuff terom@153: irc_conn_destroy(net->conn); terom@153: terom@153: net->conn = NULL; terom@153: } terom@153: terom@153: return ERROR_CODE(err); terom@153: } terom@153: terom@153: /** terom@153: * Our sock_*_connect_async callback. If the connect ended up failing, then try and reconnect later. Otherwise, do terom@153: * irc_net_connected(). terom@153: */ terom@156: static void irc_net_on_connect (transport_t *transport, void *arg) terom@153: { terom@153: struct irc_net *net = arg; terom@156: error_t err; terom@153: terom@156: // yay terom@156: if (irc_net_connected(net, transport, &err)) terom@177: log_error(&err, "irc_net_connected"); terom@156: } terom@153: terom@156: static void irc_net_on_connect_error (transport_t *transport, const error_t *conn_err, void *arg) terom@156: { terom@156: struct irc_net *net = arg; terom@156: error_t err; terom@153: terom@156: // clean up terom@156: transport_destroy(transport); terom@156: terom@156: // attempt reconnect later terom@177: log_error(conn_err, "connect failed"); terom@156: terom@156: if (irc_net_connect(net, false, &err)) terom@177: log_error(&err, "unable to reconnect"); terom@153: } terom@153: terom@156: static const struct transport_callbacks irc_net_transport_callbacks = { terom@156: .on_connect = irc_net_on_connect, terom@156: .on_error = irc_net_on_connect_error, terom@156: }; terom@156: terom@153: /** terom@153: * The low-level connect() implementation, connects based on irc_net::info, calling irc_net_connected/irc_net_reconnect terom@153: * later if this succeeds. terom@153: */ terom@153: static err_t irc_net_do_connect (struct irc_net *net, struct error_info *err) terom@153: { terom@153: struct irc_net_info *info = &net->info; terom@156: struct transport_info transport_info = { &irc_net_transport_callbacks, net, TRANSPORT_READ | TRANSPORT_WRITE }; terom@156: transport_t *transport = NULL; terom@153: terom@153: // sanity check terom@153: assert(!net->connecting && !net->connected); terom@153: terom@153: // connect based on what's known terom@156: if (info->transport) { terom@156: log_debug("connected using raw transport: %p", info->transport); terom@153: terom@156: // direct transport connection terom@156: transport = info->transport; terom@153: terom@156: // invalidate it from info since it will get destroyed later terom@156: info->transport = NULL; terom@156: terom@156: // then create the transport right away terom@156: if (irc_net_connected(net, transport, err)) terom@153: goto error; terom@153: terom@153: } else if (info->ssl_cred) { terom@153: // aquire a ref terom@153: // NOTE: before any error handling terom@180: ssl_client_cred_get(info->ssl_cred); terom@153: terom@153: log_debug("connecting to [%s]:%s using SSL", info->hostname, info->service); terom@153: terom@153: // connect terom@180: if (ssl_connect(&transport_info, &transport, info->hostname, info->service, info->ssl_cred, err)) terom@153: goto error; terom@153: terom@153: net->connecting = true; terom@153: terom@156: } else if (info->hostname || info->service) { terom@153: log_debug("connecting to [%s]:%s", info->hostname, info->service); terom@153: terom@153: // begin async connect terom@177: if (tcp_connect(&transport_info, &transport, info->hostname, info->service, err)) terom@153: goto error; terom@153: terom@153: net->connecting = true; terom@153: terom@156: } else { terom@156: RETURN_SET_ERROR_STR(err, ERR_MISC, "no connection info specified"); terom@156: terom@153: } terom@153: terom@153: return SUCCESS; terom@153: terom@153: error: terom@153: return ERROR_CODE(err); terom@153: } terom@153: terom@153: /** terom@153: * Reconnect timer callback terom@153: */ terom@153: static void irc_net_reconnect_timer_cb (int fd, short what, void *arg) terom@153: { terom@153: struct irc_net *net = arg; terom@153: struct error_info err; terom@153: terom@153: (void) fd; terom@153: (void) what; terom@153: terom@153: // execute it? terom@153: if (irc_net_connect(net, true, &err)) terom@177: log_error(&err, "unable to reconnect"); terom@153: } terom@153: terom@153: /** terom@153: * Schedule a reconnection attempt in IRC_NET_RECONNECT_INTERVAL. terom@153: */ terom@153: static err_t irc_net_schedule_reconnect (struct irc_net *net, struct error_info *err) terom@153: { terom@153: // ...for x seconds terom@153: struct timeval tv = { IRC_NET_RECONNECT_INTERVAL, 0 }; terom@153: terom@153: // schedule terom@153: if (event_add(net->reconnect_timer, &tv)) terom@153: return SET_ERROR(err, ERR_EVENT_ADD); terom@153: terom@153: // oke terom@153: return SUCCESS; terom@153: } terom@153: terom@153: err_t irc_net_connect (struct irc_net *net, bool now, struct error_info *err) terom@153: { terom@153: // attempt to reconnect now? terom@153: if (now) { terom@153: if (irc_net_do_connect(net, err)) terom@153: // log error and continue below with schedule_reconnect terom@177: log_error(err, "reconnect failed"); terom@153: terom@153: else terom@153: // connecting, done terom@153: return SUCCESS; terom@153: } terom@153: terom@153: // schedule for later terom@153: if (irc_net_schedule_reconnect(net, err)) terom@153: goto error; terom@153: terom@153: // ok terom@153: return SUCCESS; terom@153: terom@153: error: terom@153: return ERROR_CODE(err); terom@153: } terom@153: terom@156: // XXX: to get the ev_base terom@156: #include "sock_internal.h" terom@156: terom@156: terom@153: err_t irc_net_connect_init (struct irc_net *net, struct error_info *err) terom@153: { terom@153: // look up the ev_base terom@153: struct event_base *ev_base = _sock_stream_ctx.ev_base; terom@153: terom@153: // reconnect timer terom@153: if ((net->reconnect_timer = evtimer_new(ev_base, irc_net_reconnect_timer_cb, net)) == NULL) terom@153: return SET_ERROR(err, ERR_EVENT_NEW); terom@153: terom@153: // ok terom@153: return SUCCESS; terom@153: } terom@153: terom@153: void irc_net_connect_destroy (struct irc_net *net) terom@153: { terom@153: event_free(net->reconnect_timer); terom@153: } terom@153: