2 * lib/krb5/os/sendto_kdc.c
4 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. M.I.T. makes no representations about the suitability of
20 * this software for any purpose. It is provided "as is" without express
21 * or implied warranty.
24 * Send packet to KDC for realm; wait for response, retransmitting
29 #define NEED_LOWLEVEL_IO
32 #ifdef HAVE_SYS_TIME_H
40 #include <sys/select.h>
44 * send the formatted request 'message' to a KDC for realm 'realm' and
45 * return the response (if any) in 'reply'.
47 * If the message is sent and a response is received, 0 is returned,
48 * otherwise an error code is returned.
50 * The storage for 'reply' is allocated and should be freed by the caller
54 extern int krb5_max_dgram_size;
55 extern int krb5_max_skdc_timeout;
56 extern int krb5_skdc_timeout_shift;
57 extern int krb5_skdc_timeout_1;
59 krb5_error_code INTERFACE
60 krb5_sendto_kdc (context, message, realm, reply)
62 const krb5_data * message;
63 const krb5_data * realm;
66 register int timeout, host, i;
67 struct sockaddr *addr;
70 krb5_error_code retval;
73 struct timeval waitlen;
77 * find KDC location(s) for realm
80 if (retval = krb5_locate_kdc (context, realm, &addr, &naddr))
83 return KRB5_REALM_UNKNOWN;
85 socklist = (SOCKET *)malloc(naddr * sizeof(SOCKET));
86 if (socklist == NULL) {
90 for (i = 0; i < naddr; i++)
91 socklist[i] = INVALID_SOCKET;
93 if (!(reply->data = malloc(krb5_max_dgram_size))) {
98 reply->length = krb5_max_dgram_size;
100 if (SOCKET_INITIALIZE()) { /* PC needs this for some tcp/ip stacks */
102 krb5_xfree(socklist);
108 * do exponential backoff.
111 for (timeout = krb5_skdc_timeout_1; timeout < krb5_max_skdc_timeout;
112 timeout <<= krb5_skdc_timeout_shift) {
114 for (host = 0; host < naddr; host++) {
115 /* send to the host, wait timeout seconds for a response,
117 /* cache some sockets for each host */
118 if (socklist[host] == INVALID_SOCKET) {
119 /* XXX 4.2/4.3BSD has PF_xxx = AF_xxx, so the socket
120 creation here will work properly... */
124 * The protocol specifies a particular protocol to be
125 * used with the socket. Normally only a single
126 * protocol exists to support a particular socket type
127 * within a given protocol family.
129 socklist[host] = socket(addr[host].sa_family, SOCK_DGRAM, 0);
130 if (socklist[host] == INVALID_SOCKET)
131 continue; /* try other hosts */
132 /* have a socket to send/recv from */
133 /* On BSD systems, a connected UDP socket will get connection
134 refused and net unreachable errors while an unconnected
135 socket will time out, so use connect, send, recv instead of
136 sendto, recvfrom. The connect here may return an error if
137 the destination host is known to be unreachable. */
138 if (connect(socklist[host],
139 &addr[host], sizeof(addr[host])) == SOCKET_ERROR)
142 if (send(socklist[host],
143 message->data, message->length, 0) != message->length)
147 waitlen.tv_sec = timeout;
149 FD_SET(socklist[host], &readable);
150 if (nready = select(SOCKET_NFDS(socklist[host]),
155 if (nready == SOCKET_ERROR) {
156 if (SOCKET_ERRNO == SOCKET_EINTR)
158 retval = SOCKET_ERRNO;
161 if ((cc = recv(socklist[host],
162 reply->data, reply->length, 0)) == SOCKET_ERROR)
164 /* man page says error could be:
166 ENOTSOCK: it's a socket.
167 EWOULDBLOCK: not marked non-blocking, and we selected.
169 EFAULT: we allocated the reply packet.
171 In addition, net related errors like ECONNREFUSED
172 are possble (but undocumented). Assume anything
173 other than EINTR is a permanent error for the
174 server (i.e. don't set sent = 1).
177 if (SOCKET_ERRNO == SOCKET_EINTR)
182 /* We might consider here verifying that the reply
183 came from one of the KDC's listed for that address type,
184 but that check can be fouled by some implementations of
185 some network types which might show a loopback return
186 address, for example, if the KDC is on the same host
192 } else if (nready == 0) {
196 /* not ready, go on to next server */
199 /* never were able to send to any servers; give up */
200 retval = KRB5_KDC_UNREACH;
204 retval = KRB5_KDC_UNREACH;
206 SOCKET_CLEANUP(); /* Done with sockets for now */
207 for (i = 0; i < naddr; i++)
208 if (socklist[i] != INVALID_SOCKET)
209 (void) closesocket (socklist[i]);
211 krb5_xfree(socklist);