static void read_conn(struct connection *conn) { int n; time(&conn->access); #ifdef WANT_SSL if (conn->ssl) { n = openssl_read(conn); /* openssl_read can return -EAGAIN if the SSL * connection needs a read or write. */ if (n == -EAGAIN) return; } else #endif n = recv(conn->poll->fd, conn->curp, conn->rlen, 0); if (n >= 0) { if (verbose > 1) printf("+ Read %d\n", n); conn->endp = conn->curp + n; conn->rlen -= n; *conn->endp = '\0'; if (conn->func && conn->func(conn)) fail_connection(conn); } else if (n < 0) reset_connection(conn); /* Try again */ }
/* Close all sockets, free all the socket structs, and send a RST * packet to clean up kernel state for each connection. * TODO(ncardwell): centralize error handling and ensure test errors * always result in a call to these clean-up functions, so we can make * sure to reset connections in all cases. */ static void close_all_sockets(struct state *state) { struct socket *socket = state->sockets; while (socket != NULL) { if (socket->live.fd >= 0 && !socket->is_closed) { assert(socket->script.fd >= 0); DEBUGP("closing struct state socket " "live.fd:%d script.fd:%d\n", socket->live.fd, socket->script.fd); if (close(socket->live.fd)) die_perror("close"); } if (socket->protocol == IPPROTO_TCP && !state->config->is_wire_client && reset_connection(state, socket)) { die("error reseting connection\n"); } struct socket *dead_socket = socket; socket = socket->next; socket_free(dead_socket); } }
int openssl_check_connect(struct connection *conn) { int rc = mbedtls_ssl_handshake(conn->ssl); switch (rc) { case 0: /* success */ conn->connected = 1; set_writable(conn); if (verbose) printf("Ciphersuite is %s\n", mbedtls_ssl_get_ciphersuite(conn->ssl)); return 0; case MBEDTLS_ERR_SSL_WANT_READ: set_readable(conn); return 0; case MBEDTLS_ERR_SSL_WANT_WRITE: set_writable(conn); return 0; case MBEDTLS_ERR_NET_CONN_RESET: reset_connection(conn); return 1; default: printf("Not read or write 0x%x\n", rc); return 1; } }
/* Close all sockets, free all the socket structs, and send a RST * packet to clean up kernel state for each connection. * TODO(ncardwell): centralize error handling and ensure test errors * always result in a call to these clean-up functions, so we can make * sure to reset connections in all cases. */ static void close_all_sockets(struct state *state) { struct socket *socket = state->sockets; while (socket != NULL) { if (socket->live.fd >= 0 && !socket->is_closed) { assert(socket->script.fd >= 0); DEBUGP("closing struct state socket " "live.fd:%d script.fd:%d\n", socket->live.fd, socket->script.fd); if (close(socket->live.fd)) die_perror("close"); } if ((socket->state != SOCKET_INIT) && (socket->state != SOCKET_NEW) && (socket->state != SOCKET_PASSIVE_LISTENING) && (state->config->is_wire_client == false)) { switch (socket->protocol) { case IPPROTO_TCP: if (reset_connection(state, socket) != STATUS_OK) die("error reseting connection\n"); break; case IPPROTO_SCTP: if (abort_association(state, socket) != STATUS_OK) die("error aborting association\n"); break; default: break; } } struct socket *dead_socket = socket; socket = socket->next; socket_free(dead_socket); } }
static void read_cb(int fd, void *arg) { struct argos_net_conn *conn = arg; /* * We only want to do reads if conn->state == NET_CONN_CONNECTED, but we * don't assert() this because its possible for this socket to be selected * simultaneously for both a read and a write and then for our state to * change during the write attempt. */ if (conn->state != ARGOS_NET_CONN_CONNECTED) return; ssize_t len = recv(conn->sock, buffer_tail(conn->inbuf), buffer_remaining(conn->inbuf), 0); if (len == -1) { if (IS_NETWORK_ERROR(errno)) { /* network error; reset our connection */ orion_log_warn_errno("recv"); reset_connection(conn, 0); } else if (errno == EINTR) { /* don't care; ignore it */ } else { /* anything else is a fatal error */ orion_log_crit_errno("recv"); kill_connection(conn); orion_log_crit("unexpected recv() error; connection is now dead"); } } else if (len == 0) { /* EOF received (maybe other end is shutting down?) */ orion_log_info("EOF received from remote end - closing socket"); if (buffer_len(conn->inbuf) > 0) orion_log_warn("incomplete message received (inbuflen=%d)", buffer_len(conn->inbuf)); reset_connection(conn, 1 /* flush buffers */); } else { /* ok, we read some data into the inbuf; update the buffer */ int rv = buffer_expand(conn->inbuf, len); if (rv == -1) KABOOM("buffer_expand"); conn->bytes_recv += len; /* now process (i.e. look for complete messages in) the inbuf */ process_inbuf(conn); } }
static ssize_t socket_send(struct argos_net_conn *conn, const void *msg, size_t len) { #if ARGOS_NET_TRACE_IO struct timeval start; if (gettimeofday(&start, NULL) != 0) { orion_log_crit_errno("gettimeofday"); return -1; } #endif /* #if ARGOS_NET_TRACE_IO */ ssize_t sentlen = send(conn->sock, msg, len, 0); if (sentlen == -1) { if (IS_NETWORK_ERROR(errno)) { /* network error; reset our connection */ orion_log_warn_errno("send() failed"); reset_connection(conn, 0); } else { /* anything else is a fatal error */ orion_log_crit_errno("send"); kill_connection(conn); orion_log_crit("unexpected send() error; connection is now dead"); } return -1; } else { /* send() succeeded */ assert(sentlen > 0); /* this variable is used even if ARGOS_NET_TRACE_IO is false */ struct timeval end; if (gettimeofday(&end, NULL) != 0) { orion_log_crit_errno("gettimeofday"); return -1; } #if ARGOS_NET_TRACE_IO struct timeval elapsed; orion_time_subtract(&end, &start, &elapsed); float elapsed_msec = elapsed.tv_sec*1000 + (float)elapsed.tv_usec/1000; orion_log_debug("sent %u bytes in %.2f ms (%.2f MB/s); requested %u", sentlen, elapsed_msec, ((sentlen/elapsed_msec)*1000)/(1024*1024), len); #endif /* #if ARGOS_NET_TRACE_IO */ conn->bytes_sent += sentlen; conn->last_send = end; conn->stall_logged = 0; return sentlen; } }
void i2c_udp_periodic(void) { if(STATSI2C.timeout == 1){ i2c_master_disable(); reset_connection(uip_udp_conn); /* FIXME: PORTC &= ~_BV(PC2); */ } if(STATSI2C.timeout > 0) STATSI2C.timeout--; /* error detection on i2c bus */ if((TWSR & 0xF8) == 0x00) i2c_master_disable(); }
static void handle_connect(struct argos_net_conn *conn) { assert(conn->state == ARGOS_NET_CONN_CONNECTING); /* * connect() completed, but did it succeed or fail? getpeername() will * tell us. reference: http://cr.yp.to/docs/connect.html */ struct sockaddr_in sin; socklen_t slen = sizeof(sin); if (getpeername(conn->sock, (struct sockaddr*)&sin, &slen) == -1) { if (errno == ENOTCONN) { /* connect failed; ok now use error slippage to get the real error */ char c; int rv = read(conn->sock, &c, 1); assert(rv == -1); handle_connect_failure(conn); } else if (errno == ECONNRESET) { /* * not sure if this can actually happen - perhaps with perfect * timing (connection lost right before we call getpeername) */ orion_log_warn_errno("getpeername"); reset_connection(conn, 0); } else { /* this is unexpected... */ orion_log_crit_errno("getpeername"); kill_connection(conn); orion_log_crit("unexpected getpeername() error after asynchronous" " connect() selected for writability; connection is now dead"); } } else { /* connect succeeded */ orion_log_info("connect() succeeded asynchronously"); handle_connect_success(conn); } }
void i2c_udp_init(uip_udp_conn_t *i2c_conn) { reset_connection(i2c_conn); }
/* * child main loop */ void do_child(int unix_fd, int inet_fd) { POOL_CONNECTION *frontend; POOL_CONNECTION_POOL *backend; struct timeval now; struct timezone tz; struct timeval timeout; static int connected; /* non 0 if has been accepted connections from frontend */ int connections_count = 0; /* used if child_max_connections > 0 */ int found; char psbuf[NI_MAXHOST + 128]; pool_debug("I am %d", getpid()); /* Identify myself via ps */ init_ps_display("", "", "", ""); /* set up signal handlers */ signal(SIGALRM, SIG_DFL); signal(SIGTERM, die); signal(SIGINT, die); signal(SIGHUP, reload_config_handler); signal(SIGQUIT, die); signal(SIGCHLD, SIG_DFL); signal(SIGUSR1, close_idle_connection); signal(SIGUSR2, wakeup_handler); signal(SIGPIPE, SIG_IGN); #ifdef NONE_BLOCK /* set listen fds to none-blocking */ pool_set_nonblock(unix_fd); if (inet_fd) { pool_set_nonblock(inet_fd); } #endif /* Initialize my backend status */ pool_initialize_private_backend_status(); /* Initialize per process context */ pool_init_process_context(); /* initialize random seed */ gettimeofday(&now, &tz); #if defined(sun) || defined(__sun) srand((unsigned int) now.tv_usec); #else srandom((unsigned int) now.tv_usec); #endif /* initialize system db connection */ init_system_db_connection(); /* initialize connection pool */ if (pool_init_cp()) { child_exit(1); } /* * Open pool_passwd in child process. This is necessary to avoid the * file descriptor race condition reported in [pgpool-general: 1141]. */ if (strcmp("", pool_config->pool_passwd)) { pool_reopen_passwd_file(); } timeout.tv_sec = pool_config->child_life_time; timeout.tv_usec = 0; for (;;) { StartupPacket *sp; idle = 1; /* pgpool stop request already sent? */ check_stop_request(); /* Check if restart request is set because of failback event * happend. If so, exit myself with exit code 1 to be * restarted by pgpool parent. */ if (pool_get_my_process_info()->need_to_restart) { pool_log("do_child: failback event found. restart myself."); pool_get_my_process_info()->need_to_restart = 0; child_exit(1); } accepted = 0; /* perform accept() */ frontend = do_accept(unix_fd, inet_fd, &timeout); if (frontend == NULL) /* connection request from frontend timed out */ { /* check select() timeout */ if (connected && pool_config->child_life_time > 0 && timeout.tv_sec == 0 && timeout.tv_usec == 0) { pool_debug("child life %d seconds expired", pool_config->child_life_time); /* * Doesn't need to call this. child_exit() calls it. * send_frontend_exits(); */ child_exit(2); } continue; } /* set frontend fd to blocking */ pool_unset_nonblock(frontend->fd); /* reset busy flag */ idle = 0; /* check backend timer is expired */ if (backend_timer_expired) { pool_backend_timer(); backend_timer_expired = 0; } /* read the startup packet */ retry_startup: sp = read_startup_packet(frontend); if (sp == NULL) { /* failed to read the startup packet. return to the accept() loop */ pool_close(frontend); connection_count_down(); continue; } /* cancel request? */ if (sp->major == 1234 && sp->minor == 5678) { cancel_request((CancelPacket *)sp->startup_packet); pool_close(frontend); pool_free_startup_packet(sp); connection_count_down(); continue; } /* SSL? */ if (sp->major == 1234 && sp->minor == 5679 && !frontend->ssl_active) { pool_debug("SSLRequest from client"); pool_ssl_negotiate_serverclient(frontend); goto retry_startup; } if (pool_config->enable_pool_hba) { /* * do client authentication. * Note that ClientAuthentication does not return if frontend * was rejected; it simply terminates this process. */ frontend->protoVersion = sp->major; frontend->database = strdup(sp->database); if (frontend->database == NULL) { pool_error("do_child: strdup failed: %s\n", strerror(errno)); child_exit(1); } frontend->username = strdup(sp->user); if (frontend->username == NULL) { pool_error("do_child: strdup failed: %s\n", strerror(errno)); child_exit(1); } ClientAuthentication(frontend); } /* * Ok, negotiation with frontend has been done. Let's go to the * next step. Connect to backend if there's no existing * connection which can be reused by this frontend. * Authentication is also done in this step. */ /* Check if restart request is set because of failback event * happend. If so, close idle connections to backend and make * a new copy of backend status. */ if (pool_get_my_process_info()->need_to_restart) { pool_log("do_child: failback event found. discard existing connections"); pool_get_my_process_info()->need_to_restart = 0; close_idle_connection(0); pool_initialize_private_backend_status(); } /* * if there's no connection associated with user and database, * we need to connect to the backend and send the startup packet. */ /* look for existing connection */ found = 0; backend = pool_get_cp(sp->user, sp->database, sp->major, 1); if (backend != NULL) { found = 1; /* existing connection associated with same user/database/major found. * however we should make sure that the startup packet contents are identical. * OPTION data and others might be different. */ if (sp->len != MASTER_CONNECTION(backend)->sp->len) { pool_debug("do_child: connection exists but startup packet length is not identical"); found = 0; } else if(memcmp(sp->startup_packet, MASTER_CONNECTION(backend)->sp->startup_packet, sp->len) != 0) { pool_debug("do_child: connection exists but startup packet contents is not identical"); found = 0; } if (found == 0) { /* we need to discard existing connection since startup packet is different */ pool_discard_cp(sp->user, sp->database, sp->major); backend = NULL; } } if (backend == NULL) { /* create a new connection to backend */ if ((backend = connect_backend(sp, frontend)) == NULL) { connection_count_down(); continue; } } else { /* reuse existing connection */ if (!connect_using_existing_connection(frontend, backend, sp)) continue; } connected = 1; /* show ps status */ sp = MASTER_CONNECTION(backend)->sp; snprintf(psbuf, sizeof(psbuf), "%s %s %s idle", sp->user, sp->database, remote_ps_data); set_ps_display(psbuf, false); /* * Initialize per session context */ pool_init_session_context(frontend, backend); /* Mark this connection pool is connected from frontend */ pool_coninfo_set_frontend_connected(pool_get_process_context()->proc_id, pool_pool_index()); /* query process loop */ for (;;) { POOL_STATUS status; status = pool_process_query(frontend, backend, 0); sp = MASTER_CONNECTION(backend)->sp; switch (status) { /* client exits */ case POOL_END: /* * do not cache connection if: * pool_config->connection_cahe == 0 or * database name is template0, template1, postgres or regression */ if (pool_config->connection_cache == 0 || !strcmp(sp->database, "template0") || !strcmp(sp->database, "template1") || !strcmp(sp->database, "postgres") || !strcmp(sp->database, "regression")) { reset_connection(); pool_close(frontend); pool_send_frontend_exits(backend); pool_discard_cp(sp->user, sp->database, sp->major); } else { POOL_STATUS status1; /* send reset request to backend */ status1 = pool_process_query(frontend, backend, 1); pool_close(frontend); /* if we detect errors on resetting connection, we need to discard * this connection since it might be in unknown status */ if (status1 != POOL_CONTINUE) { pool_debug("error in resetting connections. discarding connection pools..."); pool_send_frontend_exits(backend); pool_discard_cp(sp->user, sp->database, sp->major); } else pool_connection_pool_timer(backend); } break; /* error occurred. discard backend connection pool and disconnect connection to the frontend */ case POOL_ERROR: pool_log("do_child: exits with status 1 due to error"); child_exit(1); break; /* fatal error occurred. just exit myself... */ case POOL_FATAL: notice_backend_error(1); child_exit(1); break; /* not implemented yet */ case POOL_IDLE: do_accept(unix_fd, inet_fd, &timeout); pool_debug("accept while idle"); break; default: break; } if (status != POOL_CONTINUE) break; } /* Destroy session context */ pool_session_context_destroy(); /* Mark this connection pool is not connected from frontend */ pool_coninfo_unset_frontend_connected(pool_get_process_context()->proc_id, pool_pool_index()); accepted = 0; connection_count_down(); timeout.tv_sec = pool_config->child_life_time; timeout.tv_usec = 0; /* increment queries counter if necessary */ if ( pool_config->child_max_connections > 0 ) connections_count++; /* check if maximum connections count for this child reached */ if ( ( pool_config->child_max_connections > 0 ) && ( connections_count >= pool_config->child_max_connections ) ) { pool_log("child exiting, %d connections reached", pool_config->child_max_connections); send_frontend_exits(); child_exit(2); } } child_exit(0); }
//========================================================================================================== // Run multiple tests. Control messagaes are sent to tell mserver which scenario to run. After the run a // message is sent back to indicate success or failure. // Special message "bye" tells mserver to quit. //========================================================================================================== void MServer::run(int argc, char * argv[]) { bool last_failed = false; try { process_args(argc, argv); sip_connection = new SipConnection(vars[SERVER_IP], stoi(vars[SERVER_PORT])); ctrl_connection = new ControlConnection(vars[SERVER_IP], stoi(vars[CONTROL_PORT])); //-------------------------------------------------------------------------------------------------- // Run tests until a "bye" control message is received //-------------------------------------------------------------------------------------------------- while(true) { set_log_file(get_value(RUN_DIR) + "/mserver.log"); // Set the log file back to the global log file between tests //---------------------------------------------------------------------------------------------- // Get and process a control message (indicating which scenario to run, etc.) //---------------------------------------------------------------------------------------------- string ctrl_msg = ctrl_connection->get_message(); if(ctrl_msg == "bye") { break; } if(ctrl_msg == "awake?") { ctrl_connection->send_message("yes"); continue; } reset_connection(last_failed); // Go back to original ip, start listening if stopped last_failed = false; map<string, string> script_vars; process_control_message(ctrl_msg, script_vars); //---------------------------------------------------------------------------------------------- // Run the scenario //---------------------------------------------------------------------------------------------- try { ScriptReader reader(get_value(SCENARIO), script_vars); sip_connection->check_no_pending_messages(); } catch(string err) // Error caught here is a test error: report it and continue { cout << endl << err << endl; ctrl_connection->send_message(err); last_failed = true; continue; } ctrl_connection->send_message("success"); // Test succeeded as far as mserver is concerned } } catch (string err) // Error caught here is fatal, and mserver can't run anymore { if(!log_file_set) { // This will be in the directory from which the executable was run. This is to catch any messages that // occur before the run dir is known set_log_file("mserver.log"); } cout << endl << err << endl; exit(-1); } }
static void process_inbuf(struct argos_net_conn *conn) { while (buffer_len(conn->inbuf) >= sizeof(struct argos_net_minimal_msg)) { struct argos_net_minimal_msg *header = (struct argos_net_minimal_msg *)buffer_head(conn->inbuf); uint16_t msgtype = ntohs(header->msgtype); uint32_t msglen = ntohl(header->msglen); /* check that message type and length are valid */ if (ARGOS_NET_VALIDATE_MSGTYPE(msgtype) == 0) { orion_log_crit("invalid message type received; type=%hu, len=%u", msgtype, msglen); reset_connection(conn, 1 /* flush buffers */); return; } if (ARGOS_NET_VALIDATE_MSGLEN(msgtype, msglen) == 0) { orion_log_crit("invalid message len received; type=%hu, len=%u", msgtype, msglen); reset_connection(conn, 1 /* flush buffers */); return; } if (msglen > buffer_len(conn->inbuf)) { /* complete message not yet received */ if (msglen > buffer_size(conn->inbuf)) { /* error - message is bigger than the entire inbuf */ orion_log_err("inbuf too small for msgtype %hu (len=%u)", msgtype, msglen); reset_connection(conn, 1 /* flush buffers */); return; } /* wait for more bytes to arrive on socket */ break; } /* ok great, message length must be valid - process it */ switch (msgtype) { case ARGOS_NET_CLOSECONN_MSGTYPE: { struct argos_net_closeconn_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); orion_log_info("received close-connection message from server"); reset_connection(conn, 1 /* flush buffers */); break; } case ARGOS_NET_ERROR_MSGTYPE: { struct argos_net_error_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); char buf[ARGOS_NET_MAX_ERR_LEN+1]; ssize_t bodylen = msglen - sizeof(msg); rv = buffer_read(conn->inbuf, buf, bodylen); assert(rv >= 0); buf[bodylen] = '\0'; if (conn->errhandler != NULL) conn->errhandler(ntohs(msg.errnum), buf, conn->errhandler_user); else orion_log_err("[server] %s (%s)", strerror(ntohs(msg.errnum)), buf); break; } case ARGOS_NET_HANDSHAKE_MSGTYPE: { struct argos_net_handshake_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); /* sniffers should never receive handshake messages */ orion_log_crit("received handshake message from server"); reset_connection(conn, 1 /* flush buffers */); break; } case ARGOS_NET_PCAP_MSGTYPE: { struct argos_net_pcap_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); size_t bodylen = msglen - sizeof(msg); assert(bodylen == ntohl(msg.caplen)); if (conn->pkthandler == NULL) { int rv = buffer_discard(conn->inbuf, bodylen); assert(rv >= 0); break; } struct pcap_pkthdr pcap_hdr; pcap_hdr.len = ntohl(msg.pktlen); pcap_hdr.caplen = ntohl(msg.caplen); pcap_hdr.ts.tv_sec = ntohl(msg.ts_sec); pcap_hdr.ts.tv_usec = ntohl(msg.ts_usec); /* * index directly into buffer if possible (i.e. if the data * doesn't wrap in the buffer) */ if (buffer_len(conn->inbuf) >= bodylen) { conn->pkthandler(&pcap_hdr, buffer_head(conn->inbuf), conn->pkthandler_user); buffer_discard(conn->inbuf, bodylen); } else { /* data wraps; need to copy into a temporary buffer */ u_char tempbuf[ARGOS_NET_MAX_PKT_LEN]; buffer_read(conn->inbuf, tempbuf, bodylen); conn->pkthandler(&pcap_hdr, tempbuf, conn->pkthandler_user); } break; } case ARGOS_NET_SETBPF_MSGTYPE: { struct argos_net_setbpf_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); char buf[ARGOS_NET_MAX_BPF_LEN+1]; size_t bodylen = msglen - sizeof(msg); rv = buffer_read(conn->inbuf, buf, bodylen); assert(rv >= 0); buf[bodylen] = '\0'; if (conn->bpfhandler != NULL) conn->bpfhandler(buf, conn->bpfhandler_user); /* else, just discard and ignore */ break; } case ARGOS_NET_SETCHAN_MSGTYPE: { struct argos_net_setchan_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); if (conn->chanhandler != NULL) conn->chanhandler(ntohs(msg.chan), conn->chanhandler_user); /* else, just discard and ignore */ break; } case ARGOS_NET_STARTCLICK_MSGTYPE: { struct argos_net_startclick_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); size_t bodylen = msglen - sizeof(msg); char *click_conf = malloc(bodylen+1); if (click_conf == NULL) { char errmsg[256]; snprintf(errmsg, sizeof(errmsg), "malloc(%d): %s", bodylen, strerror(errno)); orion_log_crit_errno("malloc()"); argos_net_send_errmsg(conn, errno, errmsg); kill_connection(conn); return; } rv = buffer_read(conn->inbuf, click_conf, bodylen); assert(rv >= 0); click_conf[bodylen] = '\0'; /* is this click config any different from the last one we got? */ uint32_t key = ntohl(msg.key); if (key == conn->click_config_key) { /* keys match; we can ignore this message */ orion_log_debug("click-configuration keys match (%d)", key); } else { /* keys don't match; need to run this new configuration */ orion_log_debug("new click-configuration key: %d", key); conn->click_config_key = key; if (conn->clickhandler != NULL) conn->clickhandler(click_conf, conn->clickhandler_user); } free(click_conf); break; } case ARGOS_NET_STATS_MSGTYPE: { struct argos_net_stats_msg msg; int rv = buffer_read(conn->inbuf, &msg, sizeof(msg)); assert(rv >= 0); orion_log_warn("received stats message from server"); break; } default: orion_log_crit("process_inbuf() of net.c out of sync with net.h;" " no switch case for msgtype %hu", msgtype); kill_connection(conn); } } }
static int handle_connect_failure(struct argos_net_conn *conn) { switch (errno) { /* these errors indicate some kind of programming error */ case EBADF: case ENOTSOCK: case EAFNOSUPPORT: case EFAULT: /* fall through */ /* * generally, these are non-fatal, but should never happen in this * code; if they do, this indicates a programming error */ case EISCONN: case EALREADY: orion_log_crit_errno("connect"); kill_connection(conn); orion_log_crit("unexpected connect() error; connection is now dead"); return -1; /* these are transient errors; we retry connecting */ case EADDRNOTAVAIL: case ETIMEDOUT: case ECONNREFUSED: case ENETUNREACH: case EHOSTUNREACH: case EADDRINUSE: case ECONNRESET: /* * EAGAIN is NOT the same as EINPROGRESS; it means that the server's * connection backlog is full */ case EAGAIN: if (!conn->connect_failed) { orion_log_warn_errno("connect"); conn->connect_failed = 1; } reset_connection(conn, 0); return 0; /* these aren't really errors; the connect() should still work */ case EINPROGRESS: /* * this errno is ok if returned by connect() itself, but shouldn't be * returned via error slippage from a recv() after a failed connect */ if (conn->state == ARGOS_NET_CONN_CONNECTING) { orion_log_crit_errno("read after failed connect"); kill_connection(conn); orion_log_crit("unexpected recv() error; connection is now dead"); } /* fall through */ case EINTR: orion_log_debug("connect() in progress (\"%s\")", strerror(errno)); conn->state = ARGOS_NET_CONN_CONNECTING; return 0; default: /* unknown errno */ orion_log_crit_errno("read after failed connect"); orion_log_crit("unexpected connect errno"); kill_connection(conn); return -1; } }