void lsi_b_regsig(int sig, void (*sigfn)(int)) { #if HAVE_SIGACTION struct sigaction act; act.sa_handler = sigfn; act.sa_flags = 0; if (sigemptyset(&act.sa_mask) != 0) { WE("sigemptyset"); } else if (sigaction(sig, &act, NULL) != 0) { WE("sigaction"); } #else W("No signal support; use a real OS for that"); #endif return; }
bool writeall(int sck, char *buf, size_t len) { size_t c = 0; while (c < len) { errno = 0; ssize_t n = write(sck, buf + c, len - c); if (n == -1) { WE("failed to write"); return false; } c += n; } return true; }
/* attempt to read more data from the ircd into our read buffer. * returns 1 if something was read; 0 on timeout; -1 on failure */ static int read_more(sckhld sh, struct readctx *rctx, uint64_t to_us) { /* no sizeof rctx->workbuf here because it's one bigger than WORKBUF_SZ * and we don't want to fill the last byte with data; it's a dummy */ size_t remain = WORKBUF_SZ - (rctx->eptr - rctx->workbuf); if (!remain) { /* no more space left in receive buffer */ D("Buffer is full"); if (rctx->wptr == rctx->workbuf) { /* completely full */ E("input too long"); return -1; } /* make additional room by moving data to the beginning */ size_t datalen = (size_t)(rctx->eptr - rctx->wptr); memmove(rctx->workbuf, rctx->wptr, datalen); rctx->wptr = rctx->workbuf; rctx->eptr = &rctx->workbuf[datalen]; remain = WORKBUF_SZ - (rctx->eptr - rctx->workbuf); D("Moved %zu bytes to the front; space: %zu", datalen, remain); } V("Reading more data (max. %zu bytes, timeout: %"PRIu64, remain, to_us); long n = read_wrap(sh, rctx->eptr, remain, to_us); // >0: Amount of bytes read // 0: timeout // -1: Failure // -2: EOF if (n <= 0) { if (n < 0) n == -2 ? W("read: EOF") : WE("read failed"); else V("Timeout"); return n; } V("Got %ld more bytes", n); rctx->eptr += n; return 1; }
static void init(int *argc, char ***argv, struct settings_s *sett) { if (setvbuf(stdin, NULL, _IOLBF, 0) != 0) WE("setvbuf stdin"); if (setvbuf(stdout, NULL, _IOLBF, 0) != 0) WE("setvbuf stdout"); g_irc = irc_init(); irc_set_track(g_irc, true); irc_set_nick(g_irc, "iwat"); irc_set_uname(g_irc, "iwat"); irc_set_fname(g_irc, "irc netwat"); irc_set_conflags(g_irc, 0); irc_regcb_conread(g_irc, conread, 0); sett->heartbeat_us = DEF_HEARTBEAT_MS * 1000u; sett->cfwait_s = DEF_CONFAILWAIT_S; sett->verb = DEF_VERB; sett->scto_us = DEF_CONTO_SOFT_MS * 1000u; sett->hcto_us = DEF_CONTO_HARD_MS * 1000u; process_args(argc, argv, sett); if (!*argc) C("no server given"); for (int i = 0; i < *argc; i++) { char host[256]; uint16_t port; bool ssl = false; lsi_ut_parse_hostspec(host, sizeof host, &port, &ssl, (*argv)[i]); /* we choke on all other sorts of invalid addrs/hosts later */ struct srvlist_s *node = MALLOC(sizeof *node); if (!node) CE("malloc failed"); node->host = STRDUP(host); node->port = port; node->ssl = ssl; node->next = NULL; if (!g_srvlist) g_srvlist = node; else { struct srvlist_s *n = g_srvlist; while (n->next) n = n->next; n->next = node; } } irc_set_connect_timeout(g_irc, g_sett.scto_us, g_sett.hcto_us); D("initialized"); return; }
int addr_mksocket(const char *host, const char *service, int socktype, int aflags, conbind_t func, struct sockaddr *sockaddr, size_t *addrlen, int64_t softto_us, int64_t hardto_us) { D("invoked: host='%s', serv='%s', scktype: %d, aflags: %d, func: %s, sto=%lu, hto=%lu", host, service, socktype, aflags, !func ? "(none)" : func == connect ? "connect" : func == bind ? "bind" : "(unkonwn)", softto_us, hardto_us); struct addrinfo *ai_list = NULL; struct addrinfo hints; int64_t hardtsend = hardto_us ? tstamp_us() + hardto_us : 0; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = socktype; hints.ai_protocol = 0; hints.ai_flags = aflags; if (isdigitstr(service)) hints.ai_flags |= AI_NUMERICSERV; D("calling getaddrinfo on '%s:%s'", host, service); int r = getaddrinfo(host, service, &hints, &ai_list); if (r != 0) { W("getaddrinfo() failed: %s", gai_strerror(r)); return -1; } if (!ai_list) { W("getaddrinfo() result address list empty"); return -1; } bool success = false; int sck = -1; D("iterating over result list..."); for (struct addrinfo *ai = ai_list; ai; ai = ai->ai_next) { sck = -1; if (hardtsend && hardtsend - tstamp_us() <= 0) { W("hard timeout"); return 0; } D("next result, creating socket (fam=%d, styp=%d, prot=%d)", ai->ai_family, ai->ai_socktype, ai->ai_protocol); sck = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sck < 0) { WE("cannot create socket"); continue; } char peeraddr[64] = "(non-INET/INET6)"; unsigned short peerport = 0; if (ai->ai_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in*)ai->ai_addr; inet_ntop(AF_INET, &sin->sin_addr, peeraddr, sizeof peeraddr); peerport = ntohs(sin->sin_port); } else if (ai->ai_family == AF_INET6) { struct sockaddr_in6 *sin = (struct sockaddr_in6*)ai->ai_addr; inet_ntop(AF_INET6, &sin->sin6_addr, peeraddr, sizeof peeraddr); peerport = ntohs(sin->sin6_port); } char portstr[7]; snprintf(portstr, sizeof portstr, ":%hu", peerport); strNcat(peeraddr, portstr, sizeof peeraddr); int opt = 1; socklen_t optlen = sizeof opt; D("peer addr is '%s'", peeraddr); if (sockaddr) *sockaddr = *(ai->ai_addr); if (addrlen) *addrlen = ai->ai_addrlen; if (func) { D("going non-blocking"); if (setblocking(sck, false) == -1) { WE("failed to enable nonblocking mode"); close(sck); continue; } D("set to nonblocking mode, calling the backend function..."); errno = 0; bool doselect = false; int r = func(sck, ai->ai_addr, ai->ai_addrlen); if (r == -1 && (errno != EINPROGRESS)) { WE("backend failed"); close(sck); continue; } else if (r == -1) doselect = true; int64_t trem = 0; D("backend returned with %d", r); if (doselect) { if (hardtsend) { trem = hardtsend - tstamp_us(); if (trem <= 0) { D("hard timeout detected"); close(sck); continue; } } int64_t softtsend = softto_us ? tstamp_us() + softto_us : 0; if (softtsend) { int64_t trem_tmp = softtsend - tstamp_us(); if (trem_tmp <= 0) { W("soft timeout"); close(sck); continue; } if (trem_tmp < trem) trem = trem_tmp; } D("calling io_select1w (timeout: %lld us)", trem); r = io_select1w(sck, trem, true); if (r < 0) { WE("select() failed"); close(sck); continue; } else if (!r) { W("select() timeout"); close(sck); continue; } else D("selected!"); D("calling getsockopt to query error state"); if (getsockopt(sck, SOL_SOCKET, SO_ERROR, &opt, &optlen) != 0) { W("getsockopt failed"); close(sck); continue; } if (opt == 0) { I("socket in good shape! ('%s')", peeraddr); if (setblocking(sck, true) == -1) { WE("failed to disable nonblocking mode"); close(sck); continue; } success = true; break; } else { char errstr[256]; strerror_r(opt, errstr, sizeof errstr); W("backend function failed (%d: %s)", opt, errstr); close(sck); continue; } } else { I("There we go... ('%s')", peeraddr); if (setblocking(sck, true) == -1) { WE("failed to disable nonblocking mode"); close(sck); continue; } success = true; break; } } else { success = true; break; } } D("after loop; alling freeaddrinfo then returning %d", sck); freeaddrinfo(ai_list); return success ? sck : -1; }
int connect_socket(const char *host, unsigned short port, unsigned long to_us) { int64_t tsend = to_us ? timestamp_us() + to_us : 0; struct addrinfo *ai_list = NULL; struct addrinfo hints; memset(&hints, 0, sizeof hints); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_flags = AI_NUMERICSERV; char portstr[6]; snprintf(portstr, sizeof portstr, "%hu", port); int r = getaddrinfo(host, portstr, &hints, &ai_list); if (r != 0) { W("%s", gai_strerror(r)); return -1; } if (!ai_list) { W("result address list empty"); return -1; } int sck = -1; for (struct addrinfo *ai = ai_list; ai; ai = ai->ai_next) { errno = 0; sck = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sck < 0) { WE("cannot create socket"); continue; } errno = 0; if (fcntl(sck, F_SETFL, O_NONBLOCK) == -1) { WE("failed to enable nonblocking mode"); close(sck); sck = -1; continue; } D("set to nonblocking mode, calling connect() now"); errno = 0; int r = connect(sck, ai->ai_addr, ai->ai_addrlen); if (r == -1 && (errno != EINPROGRESS)) { WE("connect() failed"); close(sck); sck = -1; continue; } int opt = 1; socklen_t optlen = sizeof opt; struct timeval tout; tout.tv_sec = 0; tout.tv_usec = 0; int64_t trem = 0; for(;;) { if (tsend) { trem = tsend - timestamp_us(); if (trem <= 0) { W("timeout reached while in 3WHS"); close(sck); sck = -1; goto outer_bot; } tconv(&tout, &trem, false); } fd_set fds; FD_ZERO(&fds); FD_SET(sck, &fds); errno = 0; r = select(sck+1, NULL, &fds, NULL, tsend ? &tout : NULL); if (r < 0) { WE("select() failed"); close(sck); sck = -1; goto outer_bot; } if (r == 1) { D("select finished successfully"); break; } } if (getsockopt(sck, SOL_SOCKET, SO_ERROR, &opt, &optlen) != 0) { W("getsockopt failed"); close(sck); sck = -1; continue; } if (opt == 0) { D("socket connected, setting to blocking mode"); errno = 0; if (fcntl(sck, F_SETFL, 0) == -1) { WE("failed to clear nonblocking mode"); close(sck); sck = -1; continue; } break; } else { WC(opt, "could not connect socket (%d)", opt); close(sck); sck = -1; continue; } outer_bot:; } freeaddrinfo(ai_list); return sck; }