static void do_accept(struct listen_t *l) { int fd; struct client_t *c; union sockaddr_u sa; /* large enough for also IPv6 address */ socklen_t addr_len = sizeof(sa); static time_t last_EMFILE_report; char *s; if ((fd = accept(l->fd, (struct sockaddr*)&sa, &addr_len)) < 0) { int e = errno; switch (e) { /* Errors reporting really bad internal (programming) bugs */ case EBADF: case EINVAL: #ifdef ENOTSOCK case ENOTSOCK: /* Not a socket */ #endif #ifdef EOPNOTSUPP case EOPNOTSUPP: /* Not a SOCK_STREAM */ #endif #ifdef ESOCKTNOSUPPORT case ESOCKTNOSUPPORT: /* Linux errors ? */ #endif #ifdef EPROTONOSUPPORT case EPROTONOSUPPORT: /* Linux errors ? */ #endif hlog(LOG_CRIT, "accept() failed: %s (giving up)", strerror(e)); exit(1); // ABORT with core-dump ?? break; /* Too many open files -- rate limit the reporting -- every 10th second or so.. */ case EMFILE: if (last_EMFILE_report + 10 <= tick) { last_EMFILE_report = tick; hlog(LOG_ERR, "accept() failed: %s (continuing)", strerror(e)); } return; /* Errors reporting system internal/external glitches */ default: hlog(LOG_ERR, "accept() failed: %s (continuing)", strerror(e)); return; } } /* convert client address to string */ s = strsockaddr( &sa.sa, addr_len ); /* TODO: the dropped connections here are not accounted. */ /* Limit amount of connections per port, and globally. * Error messages written just before closing the socet may or may not get * to the user, but at least we try. */ if (l->portaccount->gauge >= l->clients_max || inbound_connects.gauge >= maxclients) { if (inbound_connects.gauge >= maxclients) { hlog(LOG_INFO, "%s - Denied client on fd %d from %s: MaxClients reached (%d)", l->addr_s, fd, s, inbound_connects.gauge); /* The "if" is here only to silence a compiler warning * about ignoring the result value. We're really * disconnecting the client right now, so we don't care. */ if (write(fd, "# Server full\r\n", 15)) {}; } else { hlog(LOG_INFO, "%s - Denied client on fd %d from %s: Too many clients on Listener (%d)", l->addr_s, fd, s, l->portaccount->gauge); if (write(fd, "# Port full\r\n", 13)) {}; } close(fd); hfree(s); inbound_connects_account(-1, l->portaccount); /* account rejected connection */ return; } /* match against acl... could probably have an error message to the client */ if (l->acl) { if (!acl_check(l->acl, (struct sockaddr *)&sa, addr_len)) { hlog(LOG_INFO, "%s - Denied client on fd %d from %s (ACL)", l->addr_s, fd, s); close(fd); hfree(s); inbound_connects_account(-1, l->portaccount); /* account rejected connection */ return; } } c = accept_client_for_listener(l, fd, s, &sa, addr_len); if (!c) { hlog(LOG_ERR, "%s - client_alloc returned NULL, too many clients. Denied client on fd %d from %s", l->addr_s, fd, s); close(fd); hfree(s); inbound_connects_account(-1, l->portaccount); /* account rejected connection */ return; } hfree(s); c->state = CSTATE_LOGIN; /* use the default login handler */ c->handler_line_in = &login_handler; c->keepalive = tick + keepalive_interval; #ifdef USE_SSL if (l->ssl) { if (ssl_create_connection(l->ssl, c, 0)) { close(fd); inbound_connects_account(-1, l->portaccount); /* account rejected connection */ return; } } #endif hlog(LOG_DEBUG, "%s - Accepted client on fd %d from %s", c->addr_loc, c->fd, c->addr_rem); /* set client socket options, return -1 on serious errors */ if (set_client_sockopt(c) != 0) goto err; /* ok, found it... lock the new client queue and pass the client */ if (pass_client_to_worker(pick_next_worker(), c)) goto err; return; err: inbound_connects_account(0, c->portaccount); /* something failed, remove this from accounts.. */ client_free(c); return; }
int make_uplink(struct uplink_config_t *l) { int fd, i, addrc, arg; int uplink_index; union sockaddr_u sa; /* large enough for also IPv6 address */ socklen_t addr_len; struct addrinfo *ai, *a, *ap[21]; struct addrinfo req; char *addr_s = NULL; int port; struct sockaddr *srcaddr; socklen_t srcaddr_len; memset(&req, 0, sizeof(req)); req.ai_family = 0; req.ai_socktype = SOCK_STREAM; req.ai_protocol = IPPROTO_TCP; req.ai_flags = AI_ADDRCONFIG; ai = NULL; #ifdef USE_SSL /* SSL requires both a cert and a key, or none at all */ if ((l->certfile && !l->keyfile) || (l->keyfile && !l->certfile)) { hlog(LOG_ERR, "Uplink %s: Only one of sslkey and sslcert defined - both needed for SSL authentication", l->name); return -2; } /* todo: allow triggering SSL without client auth */ if (l->keyfile && l->certfile) { if (!l->ssl) { if (config_uplink_ssl_setup(l)) { hlog(LOG_ERR, "Uplink '%s': SSL setup failed", l->name); return -2; } } } #endif /* find a free uplink slot */ for (uplink_index = 0; uplink_index < MAX_UPLINKS; uplink_index++) { if (!uplink_client[uplink_index]) break; } if (uplink_index == MAX_UPLINKS) { hlog(LOG_ERR, "Uplink %s: No available uplink slots, %d used", l->name, MAX_UPLINKS); return -2; } if (strcasecmp(l->proto, "tcp") == 0) { // well, do nothing for now. } else if (strcasecmp(l->proto, "udp") == 0) { req.ai_socktype = SOCK_DGRAM; req.ai_protocol = IPPROTO_UDP; #ifdef USE_SCTP } else if (strcasecmp(l->proto, "sctp") == 0) { req.ai_socktype = SOCK_STREAM; req.ai_protocol = IPPROTO_SCTP; #endif } else { hlog(LOG_ERR, "Uplink %s: Unsupported protocol '%s'\n", l->name, l->proto); return -2; } port = atoi(l->port); if (port < 1 || port > 65535) { hlog(LOG_ERR, "Uplink %s: unsupported port number '%s'\n", l->name, l->port); return -2; } l->state = UPLINK_ST_CONNECTING; i = getaddrinfo(l->host, l->port, &req, &ai); if (i != 0) { hlog(LOG_INFO, "Uplink %s: address resolving failure of '%s' '%s': %s", l->name, l->host, l->port, gai_strerror(i)); l->state = UPLINK_ST_NOT_LINKED; return -2; } /* Count the amount of addresses in response */ addrc = 0; for (a = ai; a && addrc < 20 ; a = a->ai_next, ++addrc) { ap[addrc] = a; /* Up to 20 first addresses */ } ap[addrc] = NULL; if (addrc == 0) { hlog(LOG_INFO, "Uplink %s: address resolving of '%s' '%s': returned 0 addresses", l->name, l->host, l->port); l->state = UPLINK_ST_NOT_LINKED; return -2; } /* Pick random address to start from */ i = random() % addrc; /* Then lets try making socket and connection in address order */ /* TODO: BUG: If the TCP connection succeeds, but the server rejects our * login due to a bad source address (like, IPv4 would be allowed but our * IPv6 address is not in the server's ACL), this currently does not switch * to the next destination address. * Instead it'll wait for the retry timer and then try a random * destination address, and eventually succeed (unless very unlucky). */ fd = -1; while ((a = ap[i])) { ap[i] = NULL; addr_s = strsockaddr(a->ai_addr, a->ai_addrlen); hlog(LOG_INFO, "Uplink %s: Connecting to %s:%s (%s) [link %d, addr %d/%d]", l->name, l->host, l->port, addr_s, uplink_index, i+1, addrc); i++; if (i == addrc) i = 0; if ((fd = socket(a->ai_family, a->ai_socktype, a->ai_protocol)) < 0) { hlog(LOG_CRIT, "Uplink %s: socket(): %s\n", l->name, strerror(errno)); hfree(addr_s); continue; } arg = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&arg, sizeof(arg))) hlog(LOG_ERR, "Uplink %s: Failed to set SO_REUSEADDR on new socket: %s", l->name, strerror(errno)); /* bind source address */ srcaddr_len = 0; if (a->ai_family == AF_INET && uplink_bind_v4_len != 0) { srcaddr = (struct sockaddr *)&uplink_bind_v4; srcaddr_len = uplink_bind_v4_len; } else if (a->ai_family == AF_INET6 && uplink_bind_v6_len != 0) { srcaddr = (struct sockaddr *)&uplink_bind_v6; srcaddr_len = uplink_bind_v6_len; } if (srcaddr_len) { if (bind(fd, srcaddr, srcaddr_len)) { char *s = strsockaddr(srcaddr, srcaddr_len); hlog(LOG_ERR, "Uplink %s: Failed to bind source address '%s': %s", l->name, s, strerror(errno)); hfree(s); goto connerr; } } /* set non-blocking mode at this point, so that we can make a * non-blocking connect() with a short timeout */ if (fcntl(fd, F_SETFL, O_NONBLOCK)) { hlog(LOG_CRIT, "Uplink %s: Failed to set non-blocking mode on new socket: %s", l->name, strerror(errno)); goto connerr; } /* Use TCP_NODELAY for APRS-IS sockets. High delays can cause packets getting past * the dupe filters. */ #ifdef TCP_NODELAY if (a->ai_protocol == IPPROTO_TCP) { int arg = 1; if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&arg, sizeof(arg))) hlog(LOG_ERR, "Uplink %s: %s: setsockopt(TCP_NODELAY, %d) failed: %s", l->name, addr_s, arg, strerror(errno)); } #endif if (connect(fd, a->ai_addr, a->ai_addrlen) && errno != EINPROGRESS) { hlog(LOG_ERR, "Uplink %s: connect(%s) failed: %s", l->name, addr_s, strerror(errno)); goto connerr; } /* Only wait a few seconds for the connection to be created. * If the connection setup is very slow, it is unlikely to * perform well enough anyway. */ struct pollfd connect_fd; connect_fd.fd = fd; connect_fd.events = POLLOUT; connect_fd.revents = 0; int r = poll(&connect_fd, 1, 3000); hlog(LOG_DEBUG, "Uplink %s: poll after connect returned %d, revents %d", l->name, r, connect_fd.revents); if (r < 0) { hlog(LOG_ERR, "Uplink %s: connect to %s: poll failed: %s", l->name, addr_s, strerror(errno)); goto connerr; } if (r < 1) { hlog(LOG_ERR, "Uplink %s: connect to %s timed out", l->name, addr_s); goto connerr; } socklen_t optlen = sizeof(arg); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&arg, &optlen) == -1) { hlog(LOG_ERR, "Uplink %s: getsockopt() after connect failed: %s", l->name, strerror(errno)); goto connerr; } else if (arg == 0) { /* Successful connect! */ hlog(LOG_DEBUG, "Uplink %s: successful connect", l->name); break; } hlog(LOG_ERR, "Uplink %s: connect to %s failed: %s", l->name, addr_s, strerror(arg)); connerr: close(fd); fd = -1; hfree(addr_s); } freeaddrinfo(ai); /* Not needed anymore.. */ if (fd < 0) { l->state = UPLINK_ST_NOT_LINKED; return -3; /* No successfull connection at any address.. */ } struct client_t *c = client_alloc(); if (!c) { hlog(LOG_ERR, "Uplink %s: client_alloc() failed, too many clients", l->name); close(fd); l->state = UPLINK_ST_NOT_LINKED; return -3; /* No successfull connection at any address.. */ } l->client_ptr = (void *)c; c->uplink_index = uplink_index; c->fd = fd; c->addr = sa; c->ai_protocol = req.ai_protocol; c->state = CSTATE_INIT; /* use the default login handler */ c->handler_line_in = &uplink_login_handler; c->flags = l->client_flags; c->keepalive = tick; c->last_read = tick; c->connect_time = now; strncpy(c->username, l->name, sizeof(c->username)); c->username[sizeof(c->username)-1] = 0; c->username_len = strlen(c->username); /* These peer/sock name calls can not fail -- or the socket closed on us in which case it gets abandoned a bit further below. */ addr_len = sizeof(sa); getpeername(fd, (struct sockaddr *)&sa, &addr_len); //s = strsockaddr( &sa.sa, addr_len ); /* server side address */ strncpy(c->addr_rem, addr_s, sizeof(c->addr_rem)); c->addr_rem[sizeof(c->addr_rem)-1] = 0; hfree(addr_s); /* hex format of client's IP address + port */ char *s = hexsockaddr( &sa.sa, addr_len ); strncpy(c->addr_hex, s, sizeof(c->addr_hex)); c->addr_hex[sizeof(c->addr_hex)-1] = 0; hfree(s); addr_len = sizeof(sa); getsockname(fd, (struct sockaddr *)&sa, &addr_len); s = strsockaddr( &sa.sa, addr_len ); /* client side address */ strncpy(c->addr_loc, s, sizeof(c->addr_loc)); c->addr_loc[sizeof(c->addr_loc)-1] = 0; hfree(s); hlog(LOG_INFO, "Uplink %s: %s: Connection established on fd %d using source address %s", l->name, c->addr_rem, c->fd, c->addr_loc); if (set_client_sockopt(c) < 0) goto err; uplink_client[uplink_index] = c; l->state = UPLINK_ST_CONNECTED; /* set up SSL if necessary */ #ifdef USE_SSL if (l->ssl) { if (ssl_create_connection(l->ssl, c, 1)) goto err; } #endif /* Push it on the first worker, which ever it is.. */ if (pass_client_to_worker(worker_threads, c)) goto err; if ((i = pthread_mutex_lock(& uplink_connects.mutex ))) hlog(LOG_ERR, "make_uplink: could not lock uplink_connects: %s", strerror(i)); ++ uplink_connects.gauge; ++ uplink_connects.counter; ++ uplink_connects.refcount; /* <-- that does not get decremented at any time.. */ if ((i = pthread_mutex_unlock(& uplink_connects.mutex ))) hlog(LOG_ERR, "make_uplink: could not unlock uplink_connects: %s", strerror(i)); c->portaccount = & uplink_connects; /* calculate traffic bytes/packets */ return 0; err: client_free(c); uplink_client[uplink_index] = NULL; l->state = UPLINK_ST_NOT_LINKED; return -1; }
static int accept_liveupgrade_single(cJSON *client, int *rxerr_map, int rxerr_map_len) { cJSON *fd, *listener_id, *username, *time_connect, *tick_connect; cJSON *state; cJSON *addr_loc; cJSON *udp_port; cJSON *app_name, *app_version; cJSON *verified; cJSON *obuf_q; cJSON *bytes_rx, *bytes_tx; cJSON *pkts_rx, *pkts_tx, *pkts_ign; cJSON *rx_errs; cJSON *filter; cJSON *ibuf, *obuf; cJSON *client_heard; cJSON *lat, *lng; unsigned addr_len; union sockaddr_u sa; char *argv[256]; int i, argc; const char *username_s = "unknown"; /* get username first, so we can log it later */ username = accept_liveupgrade_cJSON_get(client, "username", cJSON_String, username_s); if (username) username_s = username->valuestring; fd = accept_liveupgrade_cJSON_get(client, "fd", cJSON_Number, username_s); int fd_i = -1; if (fd) fd_i = fd->valueint; if (fd_i < 0) { hlog(LOG_INFO, "Live upgrade: Client '%s' has negative fd %d, ignoring (corepeer?)", username_s, fd_i); return -1; } listener_id = accept_liveupgrade_cJSON_get(client, "listener_id", cJSON_Number, username_s); state = accept_liveupgrade_cJSON_get(client, "state", cJSON_String, username_s); time_connect = accept_liveupgrade_cJSON_get(client, "t_connect", cJSON_Number, username_s); addr_loc = accept_liveupgrade_cJSON_get(client, "addr_loc", cJSON_String, username_s); app_name = accept_liveupgrade_cJSON_get(client, "app_name", cJSON_String, username_s); app_version = accept_liveupgrade_cJSON_get(client, "app_version", cJSON_String, username_s); verified = accept_liveupgrade_cJSON_get(client, "verified", cJSON_Number, username_s); obuf_q = accept_liveupgrade_cJSON_get(client, "obuf_q", cJSON_Number, username_s); bytes_rx = accept_liveupgrade_cJSON_get(client, "bytes_rx", cJSON_Number, username_s); bytes_tx = accept_liveupgrade_cJSON_get(client, "bytes_tx", cJSON_Number, username_s); pkts_rx = accept_liveupgrade_cJSON_get(client, "pkts_rx", cJSON_Number, username_s); pkts_tx = accept_liveupgrade_cJSON_get(client, "pkts_tx", cJSON_Number, username_s); pkts_ign = accept_liveupgrade_cJSON_get(client, "pkts_ign", cJSON_Number, username_s); rx_errs = accept_liveupgrade_cJSON_get(client, "rx_errs", cJSON_Array, username_s); filter = accept_liveupgrade_cJSON_get(client, "filter", cJSON_String, username_s); /* optional */ tick_connect = cJSON_GetObjectItem(client, "t_connect_tick"); udp_port = cJSON_GetObjectItem(client, "udp_port"); ibuf = cJSON_GetObjectItem(client, "ibuf"); obuf = cJSON_GetObjectItem(client, "obuf"); client_heard = cJSON_GetObjectItem(client, "client_heard"); lat = cJSON_GetObjectItem(client, "lat"); lng = cJSON_GetObjectItem(client, "lng"); if (!( (fd) && (listener_id) && (state) && (username) && (time_connect) && (addr_loc) && (app_name) && (app_version) && (verified) && (obuf_q) && (bytes_rx) && (bytes_tx) && (pkts_rx) && (pkts_tx) && (pkts_ign) && (rx_errs) && (filter) )) { hlog(LOG_ERR, "Live upgrade: Fields missing from client JSON, discarding client fd %d", fd_i); if (fd_i >= 0) close(fd_i); return -1; } hlog(LOG_DEBUG, "Old client on fd %d: %s", fd->valueint, username->valuestring); /* fetch peer address from the fd instead of parsing it from text */ addr_len = sizeof(sa); if (getpeername(fd->valueint, &sa.sa, &addr_len) != 0) { /* Sometimes clients disconnect during upgrade, especially on slow RPi servers... */ if (errno == ENOTCONN) hlog(LOG_INFO, "Live upgrade: Client %s on fd %d has disconnected during upgrade (%s)", username->valuestring, fd->valueint, strerror(errno)); else hlog(LOG_ERR, "Live upgrade: getpeername client fd %d failed: %s", fd->valueint, strerror(errno)); close(fd->valueint); return -1; } /* convert client address to string */ char *client_addr_s = strsockaddr( &sa.sa, addr_len ); /* find the right listener for this client, for configuration and accounting */ struct listen_t *l = liveupgrade_find_listener(listener_id->valueint); if (!l) { hlog(LOG_INFO, "Live upgrade: Listener has been removed for fd %d (%s - local %s): disconnecting %s", fd->valueint, client_addr_s, addr_loc->valuestring, username->valuestring); close(fd->valueint); hfree(client_addr_s); return -1; } struct client_t *c = accept_client_for_listener(l, fd->valueint, client_addr_s, &sa, addr_len); if (!c) { hlog(LOG_ERR, "Live upgrade - client_alloc returned NULL, too many clients. Denied client %s on fd %d from %s", username->valuestring, fd->valueint, client_addr_s); close(fd->valueint); hfree(client_addr_s); return -1; } hfree(client_addr_s); if (strcmp(state->valuestring, "connected") == 0) { c->state = CSTATE_CONNECTED; c->handler_line_in = &incoming_handler; strncpy(c->username, username->valuestring, sizeof(c->username)); c->username[sizeof(c->username)-1] = 0; c->username_len = strlen(c->username); } else if (strcmp(state->valuestring, "login") == 0) { c->state = CSTATE_LOGIN; c->handler_line_in = &login_handler; } else { hlog(LOG_ERR, "Live upgrade: Client %s is in invalid state '%s' (fd %d)", l->addr_s, state->valuestring, l->fd); goto err; } /* distribute keepalive intervals for the existing old clients * but send them rather sooner than later */ // coverity[dont_call] // squelch warning: not security sensitive use of random(): load distribution c->keepalive = tick + (random() % (keepalive_interval/2)); /* distribute cleanup intervals over the next 2 minutes */ // coverity[dont_call] // squelch warning: not security sensitive use of random(): load distribution c->cleanup = tick + (random() % 120); c->connect_time = time_connect->valueint; /* live upgrade / backward compatibility: upgrading from <= 1.8.2 requires the 'else' path' */ if (tick_connect && tick_connect->type == cJSON_Number) c->connect_tick = tick_connect->valueint; else /* convert to monotonic time */ c->connect_tick = tick - (now - c->connect_time); c->validated = verified->valueint; c->localaccount.rxbytes = bytes_rx->valuedouble; c->localaccount.txbytes = bytes_tx->valuedouble; c->localaccount.rxpackets = pkts_rx->valuedouble; c->localaccount.txpackets = pkts_tx->valuedouble; c->localaccount.rxdrops = pkts_ign->valuedouble; login_set_app_name(c, app_name->valuestring, app_version->valuestring); // handle client's filter setting if (c->flags & CLFLAGS_USERFILTEROK && (filter) && (filter->valuestring) && *(filter->valuestring)) { // archive a copy of the filters, for status display strncpy(c->filter_s, filter->valuestring, FILTER_S_SIZE); c->filter_s[FILTER_S_SIZE-1] = 0; sanitize_ascii_string(c->filter_s); char *f = hstrdup(filter->valuestring); argc = parse_args(argv, f); for (i = 0; i < argc; ++i) { filter_parse(c, argv[i], 1); } hfree(f); } // set up UDP downstream if necessary if (udp_port && udp_port->type == cJSON_Number && udp_port->valueint > 1024 && udp_port->valueint < 65536) { if (login_setup_udp_feed(c, udp_port->valueint) != 0) { hlog(LOG_DEBUG, "%s/%s: Requested UDP on client port with no UDP configured", c->addr_rem, c->username); } } // fill up ibuf if (ibuf && ibuf->type == cJSON_String && ibuf->valuestring) { int l = hex_decode(c->ibuf, c->ibuf_size, ibuf->valuestring); if (l < 0) { hlog(LOG_ERR, "Live upgrade: %s/%s: Failed to decode ibuf: %s", c->addr_rem, c->username, ibuf->valuestring); } else { c->ibuf_end = l; hlog(LOG_DEBUG, "Live upgrade: Decoded ibuf %d bytes: '%.*s'", l, l, c->ibuf); hlog(LOG_DEBUG, "Hex: %s", ibuf->valuestring); } } // fill up obuf if (obuf && obuf->type == cJSON_String && obuf->valuestring) { int l = hex_decode(c->obuf, c->obuf_size, obuf->valuestring); if (l < 0) { hlog(LOG_ERR, "Live upgrade: %s/%s: Failed to decode obuf: %s", c->addr_rem, c->username, obuf->valuestring); } else { c->obuf_start = 0; c->obuf_end = l; hlog(LOG_DEBUG, "Live upgrade: Decoded obuf %d bytes: '%.*s'", l, l, c->obuf); hlog(LOG_DEBUG, "Hex: %s", obuf->valuestring); } } /* load list of stations heard by this client, to immediately support * messaging */ if (client_heard && client_heard->type == cJSON_Array) client_heard_json_load(c, client_heard); /* load rxerrs counters, with error name string mapping to support * adding/reordering of error counters */ if (rx_errs && rx_errs->type == cJSON_Array && rxerr_map && rxerr_map_len > 0) accept_rx_err_load(c, rx_errs, rxerr_map, rxerr_map_len); /* set client lat/lon, if they're given */ if (lat && lng && lat->type == cJSON_Number && lng->type == cJSON_Number) { c->loc_known = 1; c->lat = lat->valuedouble; c->lng = lng->valuedouble; } hlog(LOG_DEBUG, "%s - Accepted live upgrade client on fd %d from %s", c->addr_loc, c->fd, c->addr_rem); /* set client socket options, return -1 on serious errors */ if (set_client_sockopt(c) != 0) goto err; /* Add the client to the client list. */ int old_fd = clientlist_add(c); if (c->validated && old_fd != -1) { /* TODO: If old connection is SSL validated, and this one is not, do not disconnect it. */ hlog(LOG_INFO, "fd %d: Disconnecting duplicate validated client with username '%s'", old_fd, c->username); shutdown(old_fd, SHUT_RDWR); } /* ok, found it... lock the new client queue and pass the client */ if (pass_client_to_worker(pick_next_worker(), c)) goto err; return 0; err: close(c->fd); inbound_connects_account(0, c->portaccount); /* something failed, remove this from accounts.. */ client_free(c); return -1; }