From: Ken Raeburn Date: Tue, 18 Jun 2002 23:46:32 +0000 (+0000) Subject: Client-side TCP support update for Windows. X-Git-Tag: krb5-1.3-alpha1~681 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=96fa6cea53c9cfe3c0d835f4a8f01260d8544267;p=krb5.git Client-side TCP support update for Windows. Some support for scatter/gather socket i/o added via macros in port-sockets.h. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@14538 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/include/ChangeLog b/src/include/ChangeLog index c69a621f3..fbb8704b5 100644 --- a/src/include/ChangeLog +++ b/src/include/ChangeLog @@ -1,3 +1,16 @@ +2002-06-18 Ken Raeburn + + * port-sockets.h (sg_buf): New typedef name for OS-specific + scatter-gather buffer handle type. + (SG_ADVANCE, SG_LEN, SG_SET): New macros to manipulate sg_buf. + (SOCKET_WRITEV, SOCKET_WRITEV_TEMP): New macros for sending on + socket with gathered input. + (SHUTDOWN_READ, SHUTDOWN_WRITE, SHUTDOWN_BOTH): New macros, to be + passed to shutdown() to indicate direction. + (EINPROGRESS, EWOULDBLOCK, ECONNRESET, ECONNABORTED, + ECONNREFUSED, EHOSTUNREACH, ETIMEDOUT) [_WIN32]: Define as WSA + equivalents if not already defined. + 2002-06-15 Ken Raeburn * krb5.hin: Delete inclusion of profile.h again. diff --git a/src/include/port-sockets.h b/src/include/port-sockets.h index 052b40270..0681ddb0b 100644 --- a/src/include/port-sockets.h +++ b/src/include/port-sockets.h @@ -8,6 +8,16 @@ /* Some of our own infrastructure where the WinSock stuff was too hairy to dump into a clean Unix program... */ +typedef WSABUF sg_buf; + +#define SG_ADVANCE(SG, N) \ + ((SG)->len < (N) \ + ? (abort(), 0) \ + : ((SG)->buf += (N), (SG)->len -= (N), 0)) + +#define SG_LEN(SG) ((SG)->len + 0) +#define SG_SET(SG, B, N) ((SG)->buf = (char *)(B),(SG)->len = (N)) + #define SOCKET_INITIALIZE() 0 #define SOCKET_CLEANUP() #define SOCKET_ERRNO (WSAGetLastError()) @@ -17,6 +27,40 @@ #define SOCKET_WRITE(fd, b, l) (send(fd, b, l, 0)) #define SOCKET_EINTR WSAEINTR +/* Return -1 for error or number of bytes written. + TMP is a temporary variable; must be declared by the caller, and + must be used by this macro (to avoid compiler warnings). */ +/* WSASend returns 0 or SOCKET_ERROR. */ +#define SOCKET_WRITEV_TEMP DWORD +#define SOCKET_WRITEV(FD, SG, LEN, TMP) \ + (WSASend((FD), (SG), (LEN), &(TMP), 0, 0, 0) ? -1 : (TMP)) + +#define SHUTDOWN_READ SD_RECEIVE +#define SHUTDOWN_WRITE SD_SEND +#define SHUTDOWN_BOTH SD_BOTH + +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#ifndef ECONNRESET +#define ECONNRESET WSAECONNRESET +#endif +#ifndef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT WSAETIMEDOUT +#endif + int win_socket_initialize(); #else /* not _WIN32 */ @@ -46,6 +90,17 @@ int win_socket_initialize(); #define ioctlsocket ioctl #define SOCKET_ERROR (-1) +typedef struct iovec sg_buf; + +#define SG_ADVANCE(SG, N) \ + ((SG)->iov_len < (N) \ + ? (abort(), 0) \ + : ((SG)->iov_base = (char *) (SG)->iov_base + (N), \ + (SG)->iov_len -= (N), 0)) + +#define SG_LEN(SG) ((SG)->iov_len + 0) +#define SG_SET(SG, B, L) ((SG)->iov_base = (char*)(B), (SG)->iov_len = (L)) + /* Some of our own infrastructure where the WinSock stuff was too hairy to dump into a clean Unix program... */ @@ -57,6 +112,15 @@ int win_socket_initialize(); #define SOCKET_READ read #define SOCKET_WRITE write #define SOCKET_EINTR EINTR +#define SOCKET_WRITEV_TEMP int +/* Use TMP to avoid compiler warnings and keep things consistent with + Windoze version. */ +#define SOCKET_WRITEV(FD, SG, LEN, TMP) \ + ((TMP) = writev((FD), (SG), (LEN))) + +#define SHUTDOWN_READ 0 +#define SHUTDOWN_WRITE 1 +#define SHUTDOWN_BOTH 2 #endif /* HAVE_MACSOCK_H */ diff --git a/src/lib/krb5/os/ChangeLog b/src/lib/krb5/os/ChangeLog index e075175e1..24a3f0b98 100644 --- a/src/lib/krb5/os/ChangeLog +++ b/src/lib/krb5/os/ChangeLog @@ -1,3 +1,31 @@ +2002-06-18 Ken Raeburn + + * sendto_kdc.c: Include sys/timeb.h on Windows. + (ENABLE_TCP): Macro deleted, always enable code. + (krb5_sendto_kdc): Don't try UDP if outgoing message is 1500 + bytes or larger. Print info on arguments if DEBUG defined. + (bogus_strerror) [_WIN32 && DEBUG]: New function. + (dperror, dfprintf): New macros. + (krb5int_sendto_udp): Use SOCKET_ERRNO where appropriate. Log + more info if DEBUG defined; use dperror and dfprintf. + (struct conn_state): Use new scatter-gather definitions from + port-sockets.h. Don't try to reuse data structures for both + input and output. + (struct select_state): Add exception fd set. + (print_fdsets) [DEBUG]: Print exception fd set; callers changed. + (getcurtime): New function. + (call_select): Call it. Pass exception fd set to select. + (make_nonblocking, iov_advance): Deleted. + (start_tcp_connection): Expand non-blocking setting inline; set + linger period to zero. Use new scatter-gather framework. Log + more info if DEBUG defined. + (service_tcp_fd): New argument indicates exception; handle + exception fd set. Print more info if DEBUG. Use SOCKET_* + macros, scatter-gather support, SHUTDOWN_* macros. Adjust for + new conn_state definitions. + (krb5int_sendto_tcp): Set up exception fd set. Adjust for new + conn_state definitions. Use new scatter-gather support. + 2002-06-11 Ken Raeburn * sendto_kdc.c: Include sys/ioctl.h and sys/filio.h only if diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c index 10fe435ca..c0628cc32 100644 --- a/src/lib/krb5/os/sendto_kdc.c +++ b/src/lib/krb5/os/sendto_kdc.c @@ -1,7 +1,7 @@ /* * lib/krb5/os/sendto_kdc.c * - * Copyright 1990,1991 by the Massachusetts Institute of Technology. + * Copyright 1990,1991,2001,2002 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -39,18 +39,15 @@ #include #endif #include "os-proto.h" +#ifdef _WIN32 +#include +#endif #ifdef _AIX #include #endif -#ifdef _WIN32 -#define ENABLE_TCP 0 /* for now */ -#else -#define ENABLE_TCP 1 -#endif - -#if defined(ENABLE_TCP) && !defined(_WIN32) +#ifndef _WIN32 /* For FIONBIO. */ #include #ifdef HAVE_SYS_FILIO_H @@ -97,9 +94,18 @@ krb5_sendto_kdc (context, message, realm, reply, use_master, tcp_only) * should probably be returned as well. */ +#ifdef DEBUG + fprintf(stderr, "krb5_sendto_kdc(%d@%p, \"", + message->length, message->data); + /* realm need not be nul-terminated */ + fwrite(realm->data, 1, realm->length, stderr); + fprintf(stderr, "\", use_master=%d, tcp_only=%d)\n", use_master, tcp_only); +#endif + retval = (use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN); if (!tcp_only + && message->length < 1500 && ! krb5_locate_kdc(context, realm, &addrs, use_master, SOCK_DGRAM)) { if (addrs.naddrs > 0) { retval = krb5int_sendto_udp (context, message, &addrs, reply); @@ -108,8 +114,6 @@ krb5_sendto_kdc (context, message, realm, reply, use_master, tcp_only) return 0; } } - -#if ENABLE_TCP if (! krb5_locate_kdc(context, realm, &addrs, use_master, SOCK_STREAM)) { if (addrs.naddrs > 0) { retval = krb5int_sendto_tcp (context, message, &addrs, reply); @@ -118,7 +122,6 @@ krb5_sendto_kdc (context, message, realm, reply, use_master, tcp_only) return 0; } } -#endif return retval; } @@ -136,19 +139,44 @@ static void debug_log_connect (int fd, struct addrinfo *ai) #endif } +#if defined(_WIN32) && defined(DEBUG) +static char *bogus_strerror (int xerr) +{ + static char buf[30]; + sprintf(buf, "[err%d]", xerr); + return buf; +} +#define strerror(S) bogus_strerror(S) +#endif + #ifdef DEBUG + +#ifdef _WIN32 +#define dperror(MSG) \ + fprintf(stderr, \ + "%s: an error occurred ... wouldn't you like to know more?\n" \ + "\tline=%d errno=%d socketerrno=%d\n", \ + (MSG), __LINE__, errno, SOCKET_ERRNO) +#else #define dperror(MSG) perror(MSG) +#endif #define dfprintf(ARGLIST) fprintf ARGLIST -#else + +static void print_fdsets (FILE *, fd_set *, fd_set *, fd_set *, int); + +#else /* ! DEBUG */ + #define dperror(MSG) ((void)(MSG)) #define dfprintf(ARGLIST) ((void)0) + #endif krb5_error_code krb5int_sendto_udp (krb5_context context, const krb5_data *message, const struct addrlist *addrs, krb5_data *reply) { - int timeout, host, i; + int host, i; + unsigned int timeout; int sent, nready; krb5_error_code retval; SOCKET *socklist; @@ -213,7 +241,7 @@ krb5int_sendto_udp (krb5_context context, const krb5_data *message, socklist[host] = socket(ai->ai_family, SOCK_DGRAM, 0); if (socklist[host] == INVALID_SOCKET) { dfprintf((stderr, "socket: %s\naf was %d\n", - strerror(errno), ai->ai_family)); + strerror(SOCKET_ERRNO), ai->ai_family)); continue; /* try other hosts */ } debug_log_connect(socklist[host], ai); @@ -228,10 +256,11 @@ krb5int_sendto_udp (krb5_context context, const krb5_data *message, dperror ("connect"); continue; } + } else { + dfprintf((stderr, "fd %d...", socklist[host])); } dfprintf((stderr, "sending...")); - if (send(socklist[host], - message->data, message->length, 0) + if (send(socklist[host], message->data, message->length, 0) != message->length) { dperror ("sendto"); continue; @@ -242,6 +271,12 @@ krb5int_sendto_udp (krb5_context context, const krb5_data *message, waitlen.tv_sec = timeout; FD_ZERO(&readable); FD_SET(socklist[host], &readable); +#ifdef DEBUG + fprintf(stderr, "selecting on one socket ["); + print_fdsets(stderr, &readable, 0, 0, SOCKET_NFDS(socklist[host])); + fprintf(stderr, " ] timeout %ld.%06ld\n", + waitlen.tv_sec, waitlen.tv_usec); +#endif if ((nready = select(SOCKET_NFDS(socklist[host]), &readable, 0, @@ -255,8 +290,7 @@ krb5int_sendto_udp (krb5_context context, const krb5_data *message, } if ((cc = recv(socklist[host], reply->data, reply->length, - 0)) == SOCKET_ERROR) - { + 0)) == SOCKET_ERROR) { /* man page says error could be: EBADF: won't happen ENOTSOCK: it's a socket. @@ -270,10 +304,14 @@ krb5int_sendto_udp (krb5_context context, const krb5_data *message, server (i.e. don't set sent = 1). */ - if (SOCKET_ERRNO == SOCKET_EINTR) + int e = SOCKET_ERRNO; +#ifdef DEBUG + dperror("recv"); +#endif + if (e == SOCKET_EINTR) sent = 1; continue; - } + } /* We might consider here verifying that the reply came from one of the KDC's listed for that address type, @@ -317,8 +355,18 @@ krb5int_sendto_udp (krb5_context context, const krb5_data *message, return retval; } -#if ENABLE_TCP /* + * Notes: + * + * Getting "connection refused" on a connected UDP socket causes + * select to indicate write capability on UNIX, but only shows up + * as an exception on Windows. (I don't think any UNIX system flags + * the error as an exception.) So we check for both, or make it + * system-specific. + * + * Always watch for responses from *any* of the servers. Eventually + * fix the UDP code to do the same. + * * To do: * - TCP NOPUSH/CORK socket options? * - error codes that don't suck @@ -334,40 +382,72 @@ struct conn_state { SOCKET fd; krb5_error_code err; enum { CONNECTING, WRITING, READING, FAILED } state; - char *buf; - size_t bufsize; unsigned char bufsizebytes[4]; - size_t bufsizebytes_read; - struct iovec iov[2]; - struct iovec *iovp; - int iov_count; + union { + /* When state is CONNECTING or WRITING, 'out' is valid. When + state is READING, 'in' is valid. When state is FAILED, + both should be ignored. */ + struct { + sg_buf sgbuf[2]; + sg_buf *sgp; + int sg_count; + } out; + struct { + size_t bufsizebytes_read; + size_t bufsize; + char *buf; + char *pos; + size_t n_left; + } in; + } x; }; struct select_state { int max, nfds; - fd_set rfds, wfds; + fd_set rfds, wfds, xfds; struct timeval end_time; }; #ifdef DEBUG static void -print_fdsets (FILE *f, fd_set *rfds, fd_set *wfds, int maxfd) +print_fdsets (FILE *f, fd_set *rfds, fd_set *wfds, fd_set *xfds, int maxfd) { int i; for (i = 0; i < maxfd; i++) { int r = FD_ISSET(i, rfds); - int w = FD_ISSET(i, wfds); - if (r || w) { + int w = wfds && FD_ISSET(i, wfds); + int x = xfds && FD_ISSET(i, xfds); + if (r || w || x) { fprintf(f, " %d", i); if (r) fprintf(f, "r"); if (w) fprintf(f, "w"); + if (x) + fprintf(f, "x"); } } } #endif +static int getcurtime (struct timeval *tvp) +{ +#ifdef _WIN32 + struct _timeb tb; + _ftime(&tb); + tvp->tv_sec = tb.time; + tvp->tv_usec = tb.millitm * 1000; + /* Can _ftime fail? */ + return 0; +#else + if (gettimeofday(tvp, 0)) { + dperror("gettimeofday"); + return errno; + } + return 0; +#endif +} + /* * Call select and return results. * Input: interesting file descriptors and absolute timeout @@ -381,11 +461,9 @@ call_select (struct select_state *in, struct select_state *out, int *sret) krb5_error_code e; *out = *in; - if (gettimeofday(&now, 0)) { - e = errno; - dperror("gettimeofday"); + e = getcurtime(&now); + if (e) return e; - } out->end_time.tv_sec -= now.tv_sec; out->end_time.tv_usec -= now.tv_usec; if (out->end_time.tv_usec < 0) { @@ -398,38 +476,27 @@ call_select (struct select_state *in, struct select_state *out, int *sret) } #ifdef DEBUG fprintf(stderr, "selecting on %d sockets [", out->nfds); - print_fdsets(stderr, &out->rfds, &out->wfds, out->max); + print_fdsets(stderr, &out->rfds, &out->wfds, &out->xfds, out->max); fprintf(stderr, " ] timeout %ld.%06ld\n", (long) out->end_time.tv_sec, (long) out->end_time.tv_usec); #endif - *sret = select(out->max, &out->rfds, &out->wfds, 0, &out->end_time); - e = errno; + *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, + &out->end_time); + e = SOCKET_ERRNO; #ifdef DEBUG fprintf(stderr, "select returns %d", *sret); if (*sret < 0) - fprintf(stderr, ", errno = %d/%s\n", e, strerror(e)); + fprintf(stderr, ", error = %d/%s\n", e, strerror(e)); else if (*sret == 0) fprintf(stderr, " (timeout)\n"); else { fprintf(stderr, ":"); - print_fdsets(stderr, &out->rfds, &out->wfds, out->max); + print_fdsets(stderr, &out->rfds, &out->wfds, &out->xfds, out->max); fprintf(stderr, "\n"); } #endif if (*sret < 0) - return errno; - return 0; -} - -static int -make_nonblocking (int fd) -{ - static const int one = 1; - - if (ioctlsocket (fd, FIONBIO, &one)) { - dperror("sendto_kdc_tcp: ioctl(FIONBIO)"); - return errno; - } + return e; return 0; } @@ -439,28 +506,32 @@ start_tcp_connection (struct conn_state *state, struct addrinfo *ai) int fd, e; state->err = 0; - state->buf = 0; - state->bufsizebytes_read = 0; - state->iovp = state->iov; - state->iov[0].iov_base = (char *) state->bufsizebytes; - state->iov[0].iov_len = 4; - state->iov[1].iov_base = 0; - state->iov[1].iov_len = 0; - state->iov_count = 2; + state->x.out.sgp = state->x.out.sgbuf; + SG_SET(&state->x.out.sgbuf[0], state->bufsizebytes, 4); + SG_SET(&state->x.out.sgbuf[1], 0, 0); + state->x.out.sg_count = 2; dfprintf((stderr, "getting stream socket in family %d...", ai->ai_family)); fd = socket(ai->ai_family, SOCK_STREAM, 0); if (fd == INVALID_SOCKET) { - state->err = errno; + state->err = SOCKET_ERRNO; dfprintf((stderr, "socket: %s connecting with af %d\n", strerror (state->err), ai->ai_family)); return -1; /* try other hosts */ } - debug_log_connect(fd, ai); /* Make it non-blocking. */ - make_nonblocking(fd); + { + static const int one = 1; + static const struct linger lopt = { 0, 0 }; + + if (ioctlsocket(fd, FIONBIO, (const void *) &one)) + dperror("sendto_kdc_tcp: ioctl(FIONBIO)"); + if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt))) + dperror("sendto_kdc_tcp: setsockopt(SO_LINGER)"); + } /* Start connecting to KDC. */ + debug_log_connect(fd, ai); e = connect(fd, ai->ai_addr, ai->ai_addrlen); if (e != 0) { /* @@ -471,7 +542,6 @@ start_tcp_connection (struct conn_state *state, struct addrinfo *ai) state->state = CONNECTING; } else { state->err = SOCKET_ERRNO; - dfprintf((stderr, "socket error %d in connect()\n", state->err)); state->state = FAILED; return -2; } @@ -489,40 +559,37 @@ start_tcp_connection (struct conn_state *state, struct addrinfo *ai) return 0; } -static void -iov_advance (struct iovec *iov, int how_far) -{ - if (iov->iov_len < how_far) - abort(); - /* Can't do math on void pointers. */ - iov->iov_base = (char *) iov->iov_base + how_far; - iov->iov_len -= how_far; -} - /* Return nonzero only if we're finished and the caller should exit its loop. This happens in two cases: We have a complete message, or the socket has closed and no others are open. */ static int service_tcp_fd (struct conn_state *conn, struct select_state *selstate, - int can_read, int can_write) + int can_read, int can_write, int exception) { krb5_error_code e = 0; int nwritten, nread; #ifdef DEBUG - /* index with can_read + 2 * can_write */ - static const char *const select_states[] = { - "cannot_read_or_write??", - "can_read", "can_write", "can_read/can_write", - }; - fprintf(stderr, "handling %s on fd %d in state %s\n", - select_states[can_read + 2 * can_write], - conn->fd, state_strings[(int) conn->state]); + { + int sep = ' '; + fprintf(stderr, "handling"); + if (can_read) + fprintf(stderr, "%cread", sep), sep = '/'; + if (can_write) + fprintf(stderr, "%cwrite", sep), sep = '/'; + if (exception) + fprintf(stderr, "%cexception", sep), sep = '/'; + if (sep == ' ') + fprintf(stderr, " no_flags?!"); + fprintf(stderr, " on fd %d in state %s\n", + conn->fd, state_strings[(int) conn->state]); + } #endif - if (!can_read && !can_write) + if (!can_read && !can_write && !exception) abort(); switch (conn->state) { + SOCKET_WRITEV_TEMP tmp; case CONNECTING: if (can_read) { @@ -530,9 +597,10 @@ service_tcp_fd (struct conn_state *conn, struct select_state *selstate, e = EINVAL /* ?? */; kill_conn: conn->state = FAILED; - shutdown(conn->fd, 2); + shutdown(conn->fd, SHUTDOWN_BOTH); FD_CLR(conn->fd, &selstate->rfds); FD_CLR(conn->fd, &selstate->wfds); + FD_CLR(conn->fd, &selstate->xfds); conn->err = e; dfprintf((stderr, "abandoning connection %d: %s\n", conn->fd, strerror(e))); @@ -540,7 +608,8 @@ service_tcp_fd (struct conn_state *conn, struct select_state *selstate, if (selstate->max == 1 + conn->fd) { while (selstate->max > 0 && ! FD_ISSET(selstate->max-1, &selstate->rfds) - && ! FD_ISSET(selstate->max-1, &selstate->wfds)) + && ! FD_ISSET(selstate->max-1, &selstate->wfds) + && ! FD_ISSET(selstate->max-1, &selstate->xfds)) selstate->max--; dfprintf((stderr, "new max_fd + 1 is %d\n", selstate->max)); } @@ -551,9 +620,35 @@ service_tcp_fd (struct conn_state *conn, struct select_state *selstate, } return e == 0; } + if (exception) { +#ifdef DEBUG + int sockerr; + size_t sockerrlen = sizeof(sockerr); +#endif + handle_exception: +#ifdef DEBUG + e = getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, + &sockerr, &sockerrlen); + if (e != 0) { + /* What to do now? */ + e = SOCKET_ERRNO; + dfprintf((stderr, + "getsockopt(SO_ERROR) on exception fd failed: %d\n", + e)); + goto kill_conn; + } + /* Okay, got the error back. Either way, kill the + connection. */ + e = sockerr; +#else + e = 1; /* need only be non-zero */ +#endif + goto kill_conn; + } /* * Connect finished -- but did it succeed or fail? + * UNIX sets can_write if failed. * Try writing, I guess, and find out. */ conn->state = WRITING; @@ -565,15 +660,17 @@ service_tcp_fd (struct conn_state *conn, struct select_state *selstate, /* Bad -- the KDC shouldn't be sending anything yet. */ goto kill_conn; } + if (exception) + goto handle_exception; - try_writing: + try_writing: dfprintf((stderr, "trying to writev %d (%d bytes) to fd %d\n", - conn->iov_count, - ((conn->iov_count == 2 ? conn->iovp[1].iov_len : 0) - + conn->iovp[0].iov_len), + 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)); - nwritten = writev(conn->fd, conn->iovp, - conn->iov_count); + nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp, + conn->x.out.sg_count, tmp); if (nwritten < 0) { e = SOCKET_ERRNO; dfprintf((stderr, "failed: %s\n", strerror(e))); @@ -581,61 +678,73 @@ service_tcp_fd (struct conn_state *conn, struct select_state *selstate, } dfprintf((stderr, "wrote %d bytes\n", nwritten)); while (nwritten) { - struct iovec *iovp = conn->iovp; - if (nwritten < iovp->iov_len) { - iov_advance(iovp, nwritten); + sg_buf *sgp = conn->x.out.sgp; + if (nwritten < SG_LEN(sgp)) { + SG_ADVANCE(sgp, nwritten); nwritten = 0; } else { - nwritten -= conn->iovp->iov_len; - conn->iovp++; - conn->iov_count--; - if (conn->iov_count == 0 && nwritten != 0) + nwritten -= SG_LEN(conn->x.out.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->iov_count == 0) { + if (conn->x.out.sg_count == 0) { /* Done writing, switch to reading. */ - shutdown(conn->fd, 1); + shutdown(conn->fd, SHUTDOWN_WRITE); 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? */ dfprintf((stderr, "switching fd %d to READING\n", conn->fd)); conn->state = READING; - conn->bufsizebytes_read = 0; + 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 (conn->bufsizebytes_read == 4) { + if (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. */ - dfprintf((stderr, "reading %ld bytes of data from fd %d\n", - (long) conn->iov[0].iov_len, conn->fd)); - nread = read(conn->fd, conn->iov[0].iov_base, - conn->iov[0].iov_len); + dfprintf((stderr, "reading %lu bytes of data from fd %d\n", + (unsigned long) 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->buf); - conn->buf = 0; + free(conn->x.in.buf); + conn->x.in.buf = 0; goto kill_conn; } - iov_advance(conn->iov, nread); - if (conn->iov[0].iov_len == 0) { + 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 = read(conn->fd, - conn->bufsizebytes + conn->bufsizebytes_read, - 4 - conn->bufsizebytes_read); + nread = SOCKET_READ(conn->fd, + conn->bufsizebytes + conn->x.in.bufsizebytes_read, + 4 - conn->x.in.bufsizebytes_read); if (nread < 0) { e = SOCKET_ERRNO; goto kill_conn; } - conn->bufsizebytes_read += nread; - if (conn->bufsizebytes_read == 4) { + conn->x.in.bufsizebytes_read += nread; + if (conn->x.in.bufsizebytes_read == 4) { unsigned long len; len = conn->bufsizebytes[0]; len = (len << 8) + conn->bufsizebytes[1]; @@ -648,11 +757,12 @@ service_tcp_fd (struct conn_state *conn, struct select_state *selstate, e = E2BIG; goto kill_conn; } - conn->bufsize = conn->iov[0].iov_len = len; - conn->buf = conn->iov[0].iov_base = malloc(len); + conn->x.in.bufsize = conn->x.in.n_left = len; + conn->x.in.buf = conn->x.in.pos = malloc(len); dfprintf((stderr, "allocated %lu byte buffer at %p\n", - len, conn->buf)); - if (conn->iov[0].iov_base == 0) { + len, conn->x.in.buf)); + if (conn->x.in.buf == 0) { + /* allocation failure */ e = errno; goto kill_conn; } @@ -687,16 +797,17 @@ service_fds (struct select_state *selstate, /* Got something on a socket, process it. */ for (i = 0; i <= selstate->max && selret > 0; i++) { - int can_read, can_write; + int can_read, can_write, exception; if (conns[i].fd == INVALID_SOCKET) continue; can_read = FD_ISSET(conns[i].fd, &sel_results.rfds); can_write = FD_ISSET(conns[i].fd, &sel_results.wfds); - if (!can_read && !can_write) + exception = FD_ISSET(conns[i].fd, &sel_results.xfds); + if (!can_read && !can_write && !exception) continue; selret--; - if (service_tcp_fd(&conns[i], selstate, can_read, can_write)) { + if (service_tcp_fd(&conns[i], selstate, can_read, can_write, exception)) { dfprintf((stderr, "service_tcp_fd says we're done\n")); *winning_conn = i; return 1; @@ -729,8 +840,8 @@ krb5int_sendto_tcp (krb5_context context, const krb5_data *message, if (conns == NULL) { return ENOMEM; } + memset(conns, 0, n_conns * sizeof(conns[i])); for (i = 0; i < n_conns; i++) { - memset(&conns[i], 0, sizeof(conns[i])); conns[i].fd = INVALID_SOCKET; } @@ -738,6 +849,7 @@ krb5int_sendto_tcp (krb5_context context, const krb5_data *message, select_state.nfds = 0; FD_ZERO(&select_state.rfds); FD_ZERO(&select_state.wfds); + FD_ZERO(&select_state.xfds); message_len_buf[0] = (message->length >> 24) & 0xff; message_len_buf[1] = (message->length >> 16) & 0xff; @@ -750,24 +862,22 @@ krb5int_sendto_tcp (krb5_context context, const krb5_data *message, continue; /* Send to the host, wait timeout seconds for a response, then move on. */ - if (start_tcp_connection (&conns[host], addrs->addrs[host])) + if (start_tcp_connection (&conns[host], addrs->addrs[host])) { continue; - conns[host].iov[0].iov_base = (char *) message_len_buf; - conns[host].iov[0].iov_len = 4; - conns[host].iov[1].iov_base = message->data; - conns[host].iov[1].iov_len = message->length; - + } + SG_SET(&conns[host].x.out.sgbuf[0], message_len_buf, 4); + SG_SET(&conns[host].x.out.sgbuf[1], message->data, message->length); + FD_SET(conns[host].fd, &select_state.rfds); FD_SET(conns[host].fd, &select_state.wfds); + FD_SET(conns[host].fd, &select_state.xfds); if (select_state.max <= conns[host].fd) select_state.max = conns[host].fd + 1; select_state.nfds++; - if (gettimeofday(&now, 0)) { - retval = errno; - dperror("gettimeofday"); + retval = getcurtime(&now); + if (retval) goto egress; - } select_state.end_time = now; select_state.end_time.tv_sec++; e = service_fds(&select_state, conns, host+1, &winning_conn); @@ -781,11 +891,9 @@ krb5int_sendto_tcp (krb5_context context, const krb5_data *message, return KRB5_KDC_UNREACH; } if (e == 0) { - if (gettimeofday(&now, 0)) { - retval = errno; - dperror("gettimeofday"); + retval = getcurtime(&now); + if (retval) goto egress; - } select_state.end_time = now; select_state.end_time.tv_sec += 30; e = service_fds(&select_state, conns, n_conns, &winning_conn); @@ -795,20 +903,19 @@ krb5int_sendto_tcp (krb5_context context, const krb5_data *message, goto egress; } /* Success! */ - reply->data = conns[winning_conn].buf; - reply->length = conns[winning_conn].bufsize; + reply->data = conns[winning_conn].x.in.buf; + reply->length = conns[winning_conn].x.in.bufsize; dfprintf((stderr, "returning %lu bytes in buffer %p\n", (unsigned long) reply->length, reply->data)); retval = 0; - conns[winning_conn].buf = 0; + conns[winning_conn].x.in.buf = 0; egress: for (i = 0; i < n_conns; i++) { if (conns[i].fd != INVALID_SOCKET) close(conns[i].fd); - if (conns[i].buf != 0) - free(conns[i].buf); + if (conns[i].state == READING && conns[i].x.in.buf != 0) + free(conns[i].x.in.buf); } free(conns); return retval; } -#endif