static int start_connection(krb5_context context, struct conn_state *state, const krb5_data *message, struct select_state *selstate, const krb5_data *realm, struct sendto_callback_info *callback_info) { int fd, e, type; static const int one = 1; static const struct linger lopt = { 0, 0 }; type = socktype_for_transport(state->addr.transport); fd = socket(state->addr.family, type, 0); if (fd == INVALID_SOCKET) return -1; /* try other hosts */ set_cloexec_fd(fd); /* Make it non-blocking. */ ioctlsocket(fd, FIONBIO, (const void *) &one); if (state->addr.transport == TCP) { setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt)); TRACE_SENDTO_KDC_TCP_CONNECT(context, &state->addr); } /* Start connecting to KDC. */ e = connect(fd, (struct sockaddr *)&state->addr.saddr, state->addr.len); if (e != 0) { /* * This is the path that should be followed for non-blocking * connections. */ if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) { state->state = CONNECTING; state->fd = fd; } else { (void) closesocket(fd); state->state = FAILED; return -2; } } else { /* * Connect returned zero even though we made it non-blocking. This * happens normally for UDP sockets, and can perhaps also happen for * TCP sockets connecting to localhost. */ state->state = WRITING; state->fd = fd; } /* * Here's where KPASSWD callback gets the socket information it needs for * a kpasswd request */ if (callback_info) { e = callback_info->pfn_callback(state->fd, callback_info->data, &state->callback_buffer); if (e != 0) { (void) closesocket(fd); state->fd = INVALID_SOCKET; state->state = FAILED; return -3; } message = &state->callback_buffer; } e = set_transport_message(state, realm, message); if (e != 0) { TRACE_SENDTO_KDC_ERROR_SET_MESSAGE(context, &state->addr, e); (void) closesocket(state->fd); state->fd = INVALID_SOCKET; state->state = FAILED; return -4; } if (state->addr.transport == UDP) { /* Send it now. */ ssize_t ret; sg_buf *sg = &state->out.sgbuf[0]; TRACE_SENDTO_KDC_UDP_SEND_INITIAL(context, &state->addr); ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0); if (ret < 0 || (size_t) ret != SG_LEN(sg)) { TRACE_SENDTO_KDC_UDP_ERROR_SEND_INITIAL(context, &state->addr, SOCKET_ERRNO); (void) closesocket(state->fd); state->fd = INVALID_SOCKET; state->state = FAILED; return -5; } else { state->state = READING; } } if (!cm_add_fd(selstate, state->fd)) { (void) closesocket(state->fd); state->fd = INVALID_SOCKET; state->state = FAILED; return -1; } if (state->state == CONNECTING || state->state == WRITING) cm_write(selstate, state->fd); else cm_read(selstate, state->fd); return 0; }
static int start_connection(krb5_context context, struct conn_state *state, struct select_state *selstate, struct sendto_callback_info *callback_info) { int fd, e; dprint("start_connection(@%p)\ngetting %s socket in family %d...", state, state->socktype == SOCK_STREAM ? "stream" : "dgram", state->family); fd = socket(state->family, state->socktype, 0); if (fd == INVALID_SOCKET) { state->err = SOCKET_ERRNO; dprint("socket: %m creating with af %d\n", state->err, state->family); return -1; /* try other hosts */ } #ifndef _WIN32 /* On Windows FD_SETSIZE is a count, not a max value. */ if (fd >= FD_SETSIZE) { closesocket(fd); state->err = EMFILE; dprint("socket: fd %d too high\n", fd); return -1; } #endif set_cloexec_fd(fd); /* Make it non-blocking. */ if (state->socktype == SOCK_STREAM) { static const int one = 1; static const struct linger lopt = { 0, 0 }; if (ioctlsocket(fd, FIONBIO, (const void *) &one)) dperror("sendto_kdc: ioctl(FIONBIO)"); if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt))) dperror("sendto_kdc: setsockopt(SO_LINGER)"); TRACE_SENDTO_KDC_TCP_CONNECT(context, state); } /* Start connecting to KDC. */ e = connect(fd, (struct sockaddr *)&state->addr, state->addrlen); if (e != 0) { /* * This is the path that should be followed for non-blocking * connections. */ if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) { state->state = CONNECTING; state->fd = fd; } else { dprint("connect failed: %m\n", SOCKET_ERRNO); (void) closesocket(fd); state->err = SOCKET_ERRNO; state->state = FAILED; return -2; } } else { /* * Connect returned zero even though we made it non-blocking. This * happens normally for UDP sockets, and can perhaps also happen for * TCP sockets connecting to localhost. */ state->state = WRITING; state->fd = fd; } dprint("new state = %s\n", state_strings[state->state]); /* * Here's where KPASSWD callback gets the socket information it needs for * a kpasswd request */ if (callback_info) { e = callback_info->pfn_callback(state, callback_info->context, &state->callback_buffer); if (e != 0) { dprint("callback failed: %m\n", e); (void) closesocket(fd); state->err = e; state->fd = INVALID_SOCKET; state->state = FAILED; return -3; } set_conn_state_msg_length(state, &state->callback_buffer); } if (state->socktype == SOCK_DGRAM) { /* Send it now. */ ssize_t ret; sg_buf *sg = &state->x.out.sgbuf[0]; TRACE_SENDTO_KDC_UDP_SEND_INITIAL(context, state); dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd); ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0); if (ret < 0 || (size_t) ret != SG_LEN(sg)) { TRACE_SENDTO_KDC_UDP_ERROR_SEND_INITIAL(context, state, SOCKET_ERRNO); dperror("sendto"); (void) closesocket(state->fd); state->fd = INVALID_SOCKET; state->state = FAILED; return -4; } else { state->state = READING; } } FD_SET(state->fd, &selstate->rfds); if (state->state == CONNECTING || state->state == WRITING) FD_SET(state->fd, &selstate->wfds); FD_SET(state->fd, &selstate->xfds); if (selstate->max <= state->fd) selstate->max = state->fd + 1; selstate->nfds++; dprint("new select vectors: %F\n", &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max); return 0; }