/* Set conn->state to READING when done; otherwise, call a cm_set_. */ static krb5_boolean service_https_write(krb5_context context, const krb5_data *realm, struct conn_state *conn, struct select_state *selstate) { k5_tls_status st; /* If this is our first time in here, set up the SSL context. */ if (conn->http.tls == NULL && !setup_tls(context, realm, conn, selstate)) { kill_conn(context, conn, selstate); return FALSE; } /* Try to transmit our request to the server. */ st = context->tls->write(context, conn->http.tls, SG_BUF(conn->out.sgp), SG_LEN(conn->out.sgbuf)); if (st == DONE) { TRACE_SENDTO_KDC_HTTPS_SEND(context, &conn->addr); cm_read(selstate, conn->fd); conn->state = READING; } else if (st == WANT_READ) { cm_read(selstate, conn->fd); } else if (st == WANT_WRITE) { cm_write(selstate, conn->fd); } else if (st == ERROR_TLS) { TRACE_SENDTO_KDC_HTTPS_ERROR_SEND(context, &conn->addr); kill_conn(context, conn, selstate); } return FALSE; }
/* Return true on usable data. */ static krb5_boolean service_tcp_read(krb5_context context, const krb5_data *realm, struct conn_state *conn, struct select_state *selstate) { ssize_t nread; int e = 0; struct incoming_message *in = &conn->in; if (in->bufsizebytes_read == 4) { /* Reading data. */ nread = SOCKET_READ(conn->fd, &in->buf[in->pos], in->n_left); if (nread <= 0) { e = nread ? SOCKET_ERRNO : ECONNRESET; TRACE_SENDTO_KDC_TCP_ERROR_RECV(context, &conn->addr, e); kill_conn(context, conn, selstate); return FALSE; } in->n_left -= nread; in->pos += nread; if (in->n_left <= 0) return TRUE; } else { /* Reading length. */ nread = SOCKET_READ(conn->fd, in->bufsizebytes + in->bufsizebytes_read, 4 - in->bufsizebytes_read); if (nread <= 0) { e = nread ? SOCKET_ERRNO : ECONNRESET; TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(context, &conn->addr, e); kill_conn(context, conn, selstate); return FALSE; } in->bufsizebytes_read += nread; if (in->bufsizebytes_read == 4) { unsigned long len = load_32_be(in->bufsizebytes); /* Arbitrary 1M cap. */ if (len > 1 * 1024 * 1024) { kill_conn(context, conn, selstate); return FALSE; } in->bufsize = in->n_left = len; in->pos = 0; in->buf = malloc(len); if (in->buf == NULL) { kill_conn(context, conn, selstate); return FALSE; } } } return FALSE; }
/* Perform next step in sending. Return true on usable data. */ static krb5_boolean service_dispatch(krb5_context context, const krb5_data *realm, struct conn_state *conn, struct select_state *selstate, int ssflags) { /* Check for a socket exception. */ if (ssflags & SSF_EXCEPTION) { kill_conn(context, conn, selstate); return FALSE; } switch (conn->state) { case CONNECTING: assert(conn->service_connect != NULL); return conn->service_connect(context, realm, conn, selstate); case WRITING: assert(conn->service_write != NULL); return conn->service_write(context, realm, conn, selstate); case READING: assert(conn->service_read != NULL); return conn->service_read(context, realm, conn, selstate); default: abort(); } }
/* Sets conn->state to READING when done. */ static krb5_boolean service_tcp_write(krb5_context context, const krb5_data *realm, struct conn_state *conn, struct select_state *selstate) { ssize_t nwritten; SOCKET_WRITEV_TEMP tmp; TRACE_SENDTO_KDC_TCP_SEND(context, &conn->addr); nwritten = SOCKET_WRITEV(conn->fd, conn->out.sgp, conn->out.sg_count, tmp); if (nwritten < 0) { TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, &conn->addr, SOCKET_ERRNO); kill_conn(context, conn, selstate); return FALSE; } while (nwritten) { sg_buf *sgp = conn->out.sgp; if ((size_t)nwritten < SG_LEN(sgp)) { SG_ADVANCE(sgp, (size_t)nwritten); nwritten = 0; } else { nwritten -= SG_LEN(sgp); conn->out.sgp++; conn->out.sg_count--; } } if (conn->out.sg_count == 0) { /* Done writing, switch to reading. */ cm_read(selstate, conn->fd); conn->state = READING; } return FALSE; }
static bool_t Detach_m( FlowMan_cl *self, FlowMan_Flow flow /* IN */, FlowMan_ConnID cid /* IN */ ) { flowman_st UNUSED *st = self->st; flow_t *f = (flow_t *)flow; conn_t *c = (conn_t *)cid; /* blow "c" from connection list */ if (!DEL_LIST(f->conns, c)) { printf("FlowMan$Detach: unknown CID %p\n", c); return False; } kill_conn(f, c); /* does implicit free() of c */ /* was this the last connection? */ if (!f->conns) { /* garbage collect the TX filter */ Netif$SetTxFilter(f->intf->card->netif, f->txhdl, NULL); PF$Dispose(f->txpf); f->txpf = NULL; /* but not the RX stuff, cos this flow might get re-used */ } return True; }
/* Return true on finished data. Call a cm_read/write function and return * false if the TLS layer needs it. Kill the connection on error. */ static krb5_boolean https_read_bytes(krb5_context context, struct conn_state *conn, struct select_state *selstate) { size_t bufsize, nread; k5_tls_status st; char *tmp; struct incoming_message *in = &conn->in; for (;;) { if (in->buf == NULL || in->bufsize - in->pos < 1024) { bufsize = in->bufsize ? in->bufsize * 2 : 8192; if (bufsize > 1024 * 1024) { kill_conn(context, conn, selstate); return FALSE; } tmp = realloc(in->buf, bufsize); if (tmp == NULL) { kill_conn(context, conn, selstate); return FALSE; } in->buf = tmp; in->bufsize = bufsize; } st = context->tls->read(context, conn->http.tls, &in->buf[in->pos], in->bufsize - in->pos - 1, &nread); if (st != DATA_READ) break; in->pos += nread; in->buf[in->pos] = '\0'; } if (st == DONE) return TRUE; if (st == WANT_READ) { cm_read(selstate, conn->fd); } else if (st == WANT_WRITE) { cm_write(selstate, conn->fd); } else if (st == ERROR_TLS) { TRACE_SENDTO_KDC_HTTPS_ERROR_RECV(context, &conn->addr); kill_conn(context, conn, selstate); } return FALSE; }
/* Process events on a UDP socket. Return true if we get a reply. */ static krb5_boolean service_udp_read(krb5_context context, const krb5_data *realm, struct conn_state *conn, struct select_state *selstate) { int nread; nread = recv(conn->fd, conn->in.buf, conn->in.bufsize, 0); if (nread < 0) { TRACE_SENDTO_KDC_UDP_ERROR_RECV(context, &conn->addr, SOCKET_ERRNO); kill_conn(context, conn, selstate); return FALSE; } conn->in.pos = nread; return TRUE; }
void watchdog (userT *user) { if ( ( (time(NULL) - user->time > MAX_IDLE) && (user->time) ) || ( (user->errors >= MAX_ERRORS) ) ) { kill_conn (user); return ; } }
/* Return true on readable, valid KKDCPP data. */ static krb5_boolean service_https_read(krb5_context context, const krb5_data *realm, struct conn_state *conn, struct select_state *selstate) { krb5_kkdcp_message *pm = NULL; krb5_data buf; const char *rep; struct incoming_message *in = &conn->in; /* Read data through the encryption layer. */ if (!https_read_bytes(context, conn, selstate)) return FALSE; /* Find the beginning of the response body. */ rep = strstr(in->buf, "\r\n\r\n"); if (rep == NULL) goto kill_conn; rep += 4; /* Decode the response. */ buf = make_data((char *)rep, in->pos - (rep - in->buf)); if (decode_krb5_kkdcp_message(&buf, &pm) != 0) goto kill_conn; /* Check and discard the message length at the front of the kerb_message * field after decoding. If it's wrong or missing, something broke. */ if (pm->kerb_message.length < 4 || load_32_be(pm->kerb_message.data) != pm->kerb_message.length - 4) { goto kill_conn; } /* Replace all of the content that we read back with just the message. */ memcpy(in->buf, pm->kerb_message.data + 4, pm->kerb_message.length - 4); in->pos = pm->kerb_message.length - 4; k5_free_kkdcp_message(context, pm); return TRUE; kill_conn: TRACE_SENDTO_KDC_HTTPS_ERROR(context, in->buf); k5_free_kkdcp_message(context, pm); kill_conn(context, conn, selstate); return FALSE; }
static int service_udp_fd(krb5_context context, struct conn_state *conn, struct select_state *selstate, int ssflags) { int nread; if (!(ssflags & (SSF_READ|SSF_EXCEPTION))) abort(); if (conn->state != READING) abort(); nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0); if (nread < 0) { TRACE_SENDTO_KDC_UDP_ERROR_RECV(context, conn, SOCKET_ERRNO); kill_conn(conn, selstate, SOCKET_ERRNO); return 0; } conn->x.in.pos = conn->x.in.buf + nread; return 1; }
/* Initialize TCP transport. */ static krb5_boolean service_tcp_connect(krb5_context context, const krb5_data *realm, struct conn_state *conn, struct select_state *selstate) { /* Check whether the connection succeeded. */ int e = get_so_error(conn->fd); if (e) { TRACE_SENDTO_KDC_TCP_ERROR_CONNECT(context, &conn->addr, e); kill_conn(context, conn, selstate); return FALSE; } conn->state = WRITING; /* Record this connection's timeout for service_fds. */ if (get_curtime_ms(&conn->endtime) == 0) conn->endtime += 10000; return conn->service_write(context, realm, conn, selstate); }
static int service_tcp_fd(krb5_context context, struct conn_state *conn, struct select_state *selstate, int ssflags) { int e = 0; ssize_t nwritten, nread; if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION))) abort(); switch (conn->state) { SOCKET_WRITEV_TEMP tmp; case CONNECTING: if (ssflags & SSF_READ) { /* Bad -- the KDC shouldn't be sending to us first. */ e = EINVAL /* ?? */; kill_conn: TRACE_SENDTO_KDC_TCP_DISCONNECT(context, conn); kill_conn(conn, selstate, e); if (e == EINVAL) { closesocket(conn->fd); conn->fd = INVALID_SOCKET; } return e == 0; } if (ssflags & SSF_EXCEPTION) { handle_exception: e = get_so_error(conn->fd); if (e) dprint("socket error on exception fd: %m", e); else dprint("no socket error info available on exception fd"); goto kill_conn; } /* * Connect finished -- but did it succeed or fail? * UNIX sets can_write if failed. * Call getsockopt to see if error pending. * * (For most UNIX systems it works to just try writing the * first time and detect an error. But Bill Dodd at IBM * reports that some version of AIX, SIGPIPE can result.) */ e = get_so_error(conn->fd); if (e) { TRACE_SENDTO_KDC_TCP_ERROR_CONNECT(context, conn, e); dprint("socket error on write fd: %m", e); goto kill_conn; } conn->state = WRITING; goto try_writing; case WRITING: if (ssflags & SSF_READ) { e = E2BIG; /* Bad -- the KDC shouldn't be sending anything yet. */ goto kill_conn; } if (ssflags & SSF_EXCEPTION) goto handle_exception; try_writing: dprint("trying to writev %d (%d bytes) to fd %d\n", conn->x.out.sg_count, ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0) + SG_LEN(&conn->x.out.sgp[0])), conn->fd); TRACE_SENDTO_KDC_TCP_SEND(context, conn); nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp, conn->x.out.sg_count, tmp); if (nwritten < 0) { e = SOCKET_ERRNO; TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, conn, e); dprint("failed: %m\n", e); goto kill_conn; } dprint("wrote %d bytes\n", nwritten); while (nwritten) { sg_buf *sgp = conn->x.out.sgp; if ((size_t) nwritten < SG_LEN(sgp)) { SG_ADVANCE(sgp, (size_t) nwritten); nwritten = 0; } else { nwritten -= SG_LEN(sgp); conn->x.out.sgp++; conn->x.out.sg_count--; if (conn->x.out.sg_count == 0 && nwritten != 0) /* Wrote more than we wanted to? */ abort(); } } if (conn->x.out.sg_count == 0) { /* Done writing, switch to reading. */ /* Don't call shutdown at this point because * some implementations cannot deal with half-closed connections.*/ FD_CLR(conn->fd, &selstate->wfds); /* Q: How do we detect failures to send the remaining data to the remote side, since we're in non-blocking mode? Will we always get errors on the reading side? */ dprint("switching fd %d to READING\n", conn->fd); conn->state = READING; conn->x.in.bufsizebytes_read = 0; conn->x.in.bufsize = 0; conn->x.in.buf = 0; conn->x.in.pos = 0; conn->x.in.n_left = 0; } return 0; case READING: if (ssflags & SSF_EXCEPTION) { if (conn->x.in.buf) { free(conn->x.in.buf); conn->x.in.buf = 0; } goto handle_exception; } if (conn->x.in.bufsizebytes_read == 4) { /* Reading data. */ dprint("reading %d bytes of data from fd %d\n", (int) conn->x.in.n_left, conn->fd); nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left); if (nread <= 0) { e = nread ? SOCKET_ERRNO : ECONNRESET; free(conn->x.in.buf); conn->x.in.buf = 0; TRACE_SENDTO_KDC_TCP_ERROR_RECV(context, conn, e); goto kill_conn; } conn->x.in.n_left -= nread; conn->x.in.pos += nread; if (conn->x.in.n_left <= 0) { /* We win! */ return 1; } } else { /* Reading length. */ nread = SOCKET_READ(conn->fd, conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read, 4 - conn->x.in.bufsizebytes_read); if (nread < 0) { TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(context, conn, e); e = SOCKET_ERRNO; goto kill_conn; } conn->x.in.bufsizebytes_read += nread; if (conn->x.in.bufsizebytes_read == 4) { unsigned long len = load_32_be (conn->x.in.bufsizebytes); dprint("received length on fd %d is %d\n", conn->fd, (int)len); /* Arbitrary 1M cap. */ if (len > 1 * 1024 * 1024) { e = E2BIG; goto kill_conn; } conn->x.in.bufsize = conn->x.in.n_left = len; conn->x.in.buf = conn->x.in.pos = malloc(len); dprint("allocated %d byte buffer at %p\n", (int) len, conn->x.in.buf); if (conn->x.in.buf == 0) { /* allocation failure */ e = ENOMEM; goto kill_conn; } } } break; default: abort(); } return 0; }