static void bsdtcp_fn_connect( void *cookie, security_handle_t *security_handle, security_status_t status) { struct sec_handle *rh = cookie; int result; if (status == S_OK) { int so_errno; socklen_t error_len = sizeof(so_errno); if (getsockopt(rh->rc->write, SOL_SOCKET, SO_ERROR, &so_errno, &error_len) == -1) { status = S_ERROR; } else if (rh->next_res && so_errno == ECONNREFUSED) { status = S_ERROR; } } switch (status) { case S_TIMEOUT: case S_ERROR: if (rh->next_res) { while (rh->next_res) { result = runbsdtcp(rh, rh->src_ip, rh->port); if (result >= 0) { rh->rc->refcnt++; rh->rs->rc->ev_write = event_create( (event_id_t)(rh->rs->rc->write), EV_WRITEFD, sec_connect_callback, rh); rh->ev_timeout = event_create(CONNECT_TIMEOUT, EV_TIME, sec_connect_timeout, rh); event_activate(rh->rs->rc->ev_write); event_activate(rh->ev_timeout); return; } } } // pass through case S_OK: if (rh->res) freeaddrinfo(rh->res); rh->res = NULL; rh->next_res = NULL; rh->src_ip = NULL; rh->port = 0; rh->connect_callback(rh->connect_arg, security_handle, status); break; default: assert(0); break; } }
event_handle_t * event_register( event_id_t data, event_type_t type, event_fn_t fn, void *arg) { event_handle_t *handle; handle = event_create(data, type, fn, arg); event_activate(handle); return handle; }
static void connect_thread_callback( void * cookie, security_handle_t * security_handle, security_status_t status) { proto_t *p = cookie; p->security_handle = security_handle; p->status = status; g_mutex_lock(protocol_mutex); p->event_handle = event_create((event_id_t)0, EV_TIME, connect_callback, p); event_activate(p->event_handle); g_mutex_unlock(protocol_mutex); }
/* * This is a callback for security_connect. After the security layer * has initiated a connection to the given host, this will be called * with a security_handle_t. * * On error, the security_status_t arg will reflect errors which can * be had via security_geterror on the handle. */ static void connect_callback( void *cookie) { proto_t *p = cookie; assert(p != NULL); if (p->event_handle) { event_release(p->event_handle); p->event_handle = 0; } proto_debug(1, _("protocol: connect_callback: p %p\n"), p); switch (p->status) { case S_OK: state_machine(p, PA_START, NULL); break; case S_TIMEOUT: security_seterror(p->security_handle, _("timeout during connect")); /* FALLTHROUGH */ case S_ERROR: /* * For timeouts or errors, retry a few times, waiting CONNECT_WAIT * seconds between each attempt. If they all fail, just return * an error back to the caller. */ if (--p->connecttries == 0) { state_machine(p, PA_ABORT, NULL); } else { proto_debug(1, _("protocol: connect_callback: p %p: retrying %s\n"), p, p->hostname); security_close(p->security_handle); /* XXX overload p->security handle to hold the event handle */ p->security_handle = (security_handle_t *)event_create(CONNECT_WAIT, EV_TIME, connect_wait_callback, p); event_activate((event_handle_t *) p->security_handle); } break; default: assert(0); break; } }
/* * bsdtcp version of a security handle allocator. Logically sets * up a network "connection". */ static void bsdtcp_connect( const char *hostname, char * (*conf_fn)(char *, void *), void (*fn)(void *, security_handle_t *, security_status_t), void * arg, void * datap) { struct sec_handle *rh; int result; char *canonname; char *service; char *src_ip; in_port_t port; struct addrinfo *res = NULL; assert(fn != NULL); assert(hostname != NULL); (void)conf_fn; /* Quiet unused parameter warning */ (void)datap; /* Quiet unused parameter warning */ auth_debug(1, _("bsdtcp: bsdtcp_connect: %s\n"), hostname); rh = g_new0(struct sec_handle, 1); security_handleinit(&rh->sech, &bsdtcp_security_driver); rh->dle_hostname = g_strdup(hostname); rh->hostname = NULL; rh->rs = NULL; rh->ev_timeout = NULL; rh->rc = NULL; result = resolve_hostname(hostname, SOCK_STREAM, &res, &canonname); if(result != 0) { dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result)); security_seterror(&rh->sech, _("resolve_hostname(%s): %s"), hostname, gai_strerror(result)); (*fn)(arg, &rh->sech, S_ERROR); if (res) freeaddrinfo(res); return; } if (canonname == NULL) { dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname); security_seterror(&rh->sech, _("resolve_hostname(%s) did not return a canonical name"), hostname); (*fn)(arg, &rh->sech, S_ERROR); if (res) freeaddrinfo(res); return; } rh->hostname = canonname; /* will be replaced */ canonname = NULL; /* steal reference */ rh->rs = tcpma_stream_client(rh, newhandle++); if (rh->rc == NULL) goto error; rh->rc->recv_security_ok = &bsd_recv_security_ok; rh->rc->prefix_packet = &bsd_prefix_packet; rh->rc->need_priv_port = 1; if (rh->rs == NULL) goto error; amfree(rh->hostname); rh->hostname = g_strdup(rh->rs->rc->hostname); if (conf_fn) { service = conf_fn("client_port", datap); if (!service || strlen(service) <= 1) service = AMANDA_SERVICE_NAME; src_ip = conf_fn("src_ip", datap); } else { service = AMANDA_SERVICE_NAME; src_ip = NULL; } port = find_port_for_service(service, "tcp"); if (port == 0) { security_seterror(&rh->sech, _("%s/tcp unknown protocol"), service); goto error; } /* * We need to open a new connection. * * XXX need to eventually limit number of outgoing connections here. */ rh->res = res; rh->next_res = res; rh->src_ip = src_ip; rh->port = port; if(rh->rc->read == -1) { int result = -1; while (rh->next_res) { result = runbsdtcp(rh, rh->src_ip, rh->port); if (result >=0 ) break; } if (result < 0) goto error; rh->rc->refcnt++; } /* * The socket will be opened async so hosts that are down won't * block everything. We need to register a write event * so we will know when the socket comes alive. * * Overload rh->rs->ev_read to provide a write event handle. * We also register a timeout. */ rh->fn.connect = &bsdtcp_fn_connect; rh->arg = rh; rh->connect_callback = fn; rh->connect_arg = arg; g_mutex_lock(security_mutex); rh->rs->rc->ev_write = event_create((event_id_t)(rh->rs->rc->write), EV_WRITEFD, sec_connect_callback, rh); rh->ev_timeout = event_create(CONNECT_TIMEOUT, EV_TIME, sec_connect_timeout, rh); event_activate(rh->rs->rc->ev_write); event_activate(rh->ev_timeout); g_mutex_unlock(security_mutex); return; error: if (res) { freeaddrinfo(res); } rh->res = NULL; rh->next_res = NULL; (*fn)(arg, &rh->sech, S_ERROR); }